207 lines
6.5 KiB
Plaintext
207 lines
6.5 KiB
Plaintext
<!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>
|