first
This commit is contained in:
206
views/login.ejs
Normal file
206
views/login.ejs
Normal file
@@ -0,0 +1,206 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Nostr Login - OIDC Bridge</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
max-width: 500px;
|
||||
margin: 50px auto;
|
||||
padding: 20px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
.container {
|
||||
background: white;
|
||||
padding: 30px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
}
|
||||
.logo {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
color: #333;
|
||||
}
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
input, textarea {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
textarea {
|
||||
height: 100px;
|
||||
resize: vertical;
|
||||
font-family: monospace;
|
||||
}
|
||||
.challenge {
|
||||
background-color: #f8f9fa;
|
||||
padding: 15px;
|
||||
border-radius: 4px;
|
||||
border-left: 4px solid #007bff;
|
||||
margin-bottom: 20px;
|
||||
word-break: break-all;
|
||||
font-family: monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
.button {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
padding: 12px 24px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.button:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
.button.secondary {
|
||||
background-color: #6c757d;
|
||||
}
|
||||
.button.secondary:hover {
|
||||
background-color: #545b62;
|
||||
}
|
||||
.error {
|
||||
color: #dc3545;
|
||||
background-color: #f8d7da;
|
||||
border: 1px solid #f5c6cb;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.help-text {
|
||||
font-size: 12px;
|
||||
color: #6c757d;
|
||||
margin-top: 5px;
|
||||
}
|
||||
.or-divider {
|
||||
text-align: center;
|
||||
margin: 20px 0;
|
||||
position: relative;
|
||||
}
|
||||
.or-divider::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 1px;
|
||||
background: #ddd;
|
||||
}
|
||||
.or-divider span {
|
||||
background: white;
|
||||
padding: 0 15px;
|
||||
color: #6c757d;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="logo">
|
||||
<h2>🔑 Sign in with Nostr</h2>
|
||||
<p>Authenticate using your Nostr identity</p>
|
||||
</div>
|
||||
|
||||
<% if (typeof error !== 'undefined') { %>
|
||||
<div class="error">
|
||||
<strong>Error:</strong> <%= error %>
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
<div class="challenge">
|
||||
<strong>Challenge to sign:</strong><br>
|
||||
<%= challenge %>
|
||||
</div>
|
||||
|
||||
<!-- Browser Extension Login -->
|
||||
<button class="button" onclick="signWithExtension()">
|
||||
🌐 Sign with Browser Extension
|
||||
</button>
|
||||
|
||||
<div class="or-divider">
|
||||
<span>or</span>
|
||||
</div>
|
||||
|
||||
<!-- Manual Login Form -->
|
||||
<form method="POST" action="<%= returnTo %>">
|
||||
<input type="hidden" name="uid" value="<%= uid %>">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="npub">Your Nostr Public Key (npub):</label>
|
||||
<input type="text" id="npub" name="npub" placeholder="npub1..." required>
|
||||
<div class="help-text">Your public Nostr identity key</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="event_json">Signed Event (JSON):</label>
|
||||
<textarea id="event_json" name="event_json" placeholder='{"kind":1,"pubkey":"...","created_at":...,"tags":[],"content":"challenge","sig":"..."}'></textarea>
|
||||
<div class="help-text">Create and sign a kind 1 event with the challenge as content</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="button">
|
||||
✍️ Verify Signature & Login
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div class="help-text" style="margin-top: 30px; text-align: center;">
|
||||
<strong>Instructions:</strong><br>
|
||||
1. Use a browser extension (recommended) or<br>
|
||||
2. Create a Nostr event with the challenge above as content<br>
|
||||
3. Sign it with your private key and paste the JSON
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
async function signWithExtension() {
|
||||
if (!window.nostr) {
|
||||
alert('No Nostr extension detected. Please install a Nostr browser extension like nos2x or Alby.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const challenge = '<%= challenge %>';
|
||||
const event = {
|
||||
kind: 1,
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
tags: [],
|
||||
content: challenge
|
||||
};
|
||||
|
||||
const signedEvent = await window.nostr.signEvent(event);
|
||||
const pubkey = await window.nostr.getPublicKey();
|
||||
const npub = window.NostrTools ? window.NostrTools.nip19.npubEncode(pubkey) : `npub${pubkey}`;
|
||||
|
||||
// Fill the form and submit
|
||||
document.getElementById('npub').value = npub;
|
||||
document.getElementById('event_json').value = JSON.stringify(signedEvent);
|
||||
document.querySelector('form').submit();
|
||||
} catch (error) {
|
||||
alert('Error signing with extension: ' + error.message);
|
||||
console.error('Extension signing error:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Load NostrTools if available for npub encoding
|
||||
if (typeof window !== 'undefined' && !window.NostrTools) {
|
||||
const script = document.createElement('script');
|
||||
script.src = 'https://unpkg.com/nostr-tools/lib/nostr.bundle.js';
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user