459 lines
14 KiB
HTML
459 lines
14 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>🔐 NOSTR_LOGIN_LITE - All Login Methods Test</title>
|
|
<style>
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
margin: 0;
|
|
padding: 20px;
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
min-height: 100vh;
|
|
color: white;
|
|
}
|
|
|
|
.container {
|
|
max-width: 1000px;
|
|
margin: 0 auto;
|
|
background: rgba(255, 255, 255, 0.1);
|
|
backdrop-filter: blur(10px);
|
|
border-radius: 20px;
|
|
padding: 30px;
|
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
h1 {
|
|
text-align: center;
|
|
margin-bottom: 30px;
|
|
background: linear-gradient(45deg, #fff, #007bff);
|
|
-webkit-background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
background-clip: text;
|
|
}
|
|
|
|
.status {
|
|
padding: 15px;
|
|
border-radius: 10px;
|
|
margin: 15px 0;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.status.logged-out {
|
|
background: rgba(255, 255, 255, 0.2);
|
|
border: 1px solid rgba(255, 255, 255, 0.3);
|
|
}
|
|
|
|
.status.logged-in {
|
|
background: rgba(76, 175, 80, 0.2);
|
|
border: 1px solid rgba(76, 175, 80, 0.5);
|
|
}
|
|
|
|
.status.loading {
|
|
background: rgba(255, 193, 7, 0.2);
|
|
border: 1px solid rgba(255, 193, 7, 0.5);
|
|
}
|
|
|
|
.button {
|
|
background: linear-gradient(45deg, #007bff, #0056b3);
|
|
color: white;
|
|
border: none;
|
|
padding: 15px 30px;
|
|
border-radius: 25px;
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
display: inline-block;
|
|
margin: 10px 5px;
|
|
text-decoration: none;
|
|
}
|
|
|
|
.button:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 4px 15px rgba(0, 123, 255, 0.4);
|
|
}
|
|
|
|
.button.secondary {
|
|
background: linear-gradient(45deg, #6c757d, #495057);
|
|
}
|
|
|
|
.button.secondary:hover {
|
|
box-shadow: 0 4px 15px rgba(108, 117, 125, 0.4);
|
|
}
|
|
|
|
.button.danger {
|
|
background: linear-gradient(45deg, #dc3545, #c82333);
|
|
}
|
|
|
|
.button.danger:hover {
|
|
box-shadow: 0 4px 15px rgba(220, 53, 69, 0.4);
|
|
}
|
|
|
|
.profile-card {
|
|
background: rgba(255, 255, 255, 0.1);
|
|
border-radius: 15px;
|
|
padding: 20px;
|
|
margin: 20px 0;
|
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
}
|
|
|
|
.profile-header {
|
|
text-align: center;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.profile-avatar {
|
|
width: 80px;
|
|
height: 80px;
|
|
border-radius: 50%;
|
|
margin: 0 auto 15px;
|
|
display: block;
|
|
background: rgba(255, 255, 255, 0.2);
|
|
}
|
|
|
|
.profile-name {
|
|
font-size: 24px;
|
|
font-weight: 700;
|
|
margin-bottom: 5px;
|
|
}
|
|
|
|
.profile-about {
|
|
font-style: italic;
|
|
opacity: 0.8;
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
.profile-pubkey {
|
|
font-family: 'Courier New', monospace;
|
|
font-size: 14px;
|
|
background: rgba(0, 0, 0, 0.2);
|
|
padding: 10px;
|
|
border-radius: 5px;
|
|
word-break: break-all;
|
|
}
|
|
|
|
.console-output {
|
|
background: rgba(0, 0, 0, 0.3);
|
|
border-radius: 10px;
|
|
padding: 20px;
|
|
margin: 20px 0;
|
|
max-height: 300px;
|
|
overflow-y: auto;
|
|
font-family: 'Courier New', monospace;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.console-entry {
|
|
margin: 5px 0;
|
|
}
|
|
|
|
.console-timestamp {
|
|
color: #ccc;
|
|
margin-right: 10px;
|
|
}
|
|
|
|
.info-section {
|
|
background: rgba(255, 255, 255, 0.1);
|
|
border-radius: 10px;
|
|
padding: 20px;
|
|
margin: 20px 0;
|
|
}
|
|
|
|
.info-section h3 {
|
|
margin-top: 0;
|
|
color: #007bff;
|
|
}
|
|
|
|
.methods-list {
|
|
list-style: none;
|
|
padding: 0;
|
|
}
|
|
|
|
.methods-list li {
|
|
background: rgba(255, 255, 255, 0.1);
|
|
margin: 10px 0;
|
|
padding: 10px 15px;
|
|
border-radius: 8px;
|
|
border-left: 4px solid #007bff;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<h1>🔐 NOSTR_LOGIN_LITE - All Login Methods Test</h1>
|
|
|
|
<div id="status" class="status logged-out">
|
|
⏳ Initializing NOSTR_LOGIN_LITE...
|
|
</div>
|
|
|
|
<div id="login-section">
|
|
<button id="launch-modal" class="button">🚀 Launch Login Modal</button>
|
|
<p style="font-size: 14px; opacity: 0.8; margin-top: 10px;">
|
|
Click to open the NOSTR_LOGIN_LITE modal and test all available login methods:
|
|
</p>
|
|
|
|
<div class="info-section">
|
|
<h3>Available Login Methods</h3>
|
|
<ul class="methods-list">
|
|
<li><strong>Browser Extension:</strong> Alby, nos2x, etc. (if installed)</li>
|
|
<li><strong>Local Key:</strong> Generate new keys or import existing private key/nsec</li>
|
|
<li><strong>Read Only:</strong> Access public content without authentication</li>
|
|
<li><strong>Nostr Connect (NIP-46):</strong> Connect to remote signing services</li>
|
|
<li><strong>DM/OTP:</strong> Secure local accounts with one-time passwords</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="profile-section" style="display: none;">
|
|
<div class="profile-card">
|
|
<div class="profile-header">
|
|
<img id="profile-picture" class="profile-avatar" src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODAiIGhlaWdodD0iODAiIHZpZXdCb3g9IjAgMCA4MCA4MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGNpcmNsZSBjeD0iNDAiIGN5PSI0MCIgcj0iNDAiIGZpbGw9IiNmNmY2ZjYiLz4KPHBhdGggZD0iTTQwIDQ1QzQ1IDQ1IDUwIDQyIDUwIDQwUzQ1IDQxIDQwIDQ1WiIgc3Ryb2tlPSIjYmJkIiBzdHJva2Utd2lkdGg9IjIiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPgo8Y2lyY2xlIGN4PSIzNSIgY3k9IjM1IiByPSIyIiBmaWxsPSIjYmJkIi8+CjxjaXJjbGUgY3g9IjQ1IiBjeT0iMzUiIHI9IjIiIGZpbGw9IiNiYmQiLz4KPGRlZnM+Cj08L2RlZnM+Cjwvc3ZnPgo=" alt="Profile Picture">
|
|
<div id="profile-name" class="profile-name">Loading...</div>
|
|
<div id="profile-about" class="profile-about"></div>
|
|
</div>
|
|
<div>
|
|
<strong>Public Key:</strong>
|
|
<div id="profile-pubkey" class="profile-pubkey"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<button id="refresh-profile" class="button secondary">🔄 Refresh Profile</button>
|
|
<button id="logout" class="button danger">🚪 Logout</button>
|
|
</div>
|
|
|
|
<div class="console-output" id="console-display">
|
|
<div class="console-entry">
|
|
<span class="console-timestamp">[Console]</span> Ready for testing
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Load the official nostr-tools bundle first -->
|
|
<script src="../lite/nostr.bundle.js"></script>
|
|
|
|
<!-- Load NOSTR_LOGIN_LITE main library (now includes NIP-46 extension) -->
|
|
<script src="../lite/nostr-lite.js"></script>
|
|
|
|
<script>
|
|
// Console logging helper
|
|
function log(level, message) {
|
|
const consoleDiv = document.getElementById('console-display');
|
|
const entry = document.createElement('div');
|
|
entry.className = 'console-entry';
|
|
|
|
const timestamp = new Date().toLocaleTimeString();
|
|
const prefix = level === 'ERROR' ? '[ERROR]' :
|
|
level === 'SUCCESS' ? '[SUCCESS]' :
|
|
level === 'WARNING' ? '[WARNING]' : '[INFO]';
|
|
|
|
entry.innerHTML = `<span class="console-timestamp">[${timestamp}] ${prefix}</span> ${message}`;
|
|
consoleDiv.appendChild(entry);
|
|
consoleDiv.scrollTop = consoleDiv.scrollHeight;
|
|
}
|
|
|
|
// Global variables
|
|
let nlLite = null;
|
|
let userPubkey = null;
|
|
let relayUrl = 'wss://relay.laantungir.net';
|
|
|
|
// Initialize NOSTR_LOGIN_LITE
|
|
async function initializeApp() {
|
|
log('INFO', 'Initializing NOSTR_LOGIN_LITE...');
|
|
|
|
try {
|
|
await window.NOSTR_LOGIN_LITE.init({
|
|
theme: 'dark',
|
|
darkMode: false,
|
|
relays: [relayUrl, 'wss://relay.damus.io'],
|
|
methods: {
|
|
extension: true,
|
|
local: true,
|
|
readonly: true,
|
|
remote: true, // Enables "Nostr Connect" (NIP-46)
|
|
otp: true // Enables "DM/OTP"
|
|
},
|
|
debug: true
|
|
});
|
|
|
|
nlLite = window.NOSTR_LOGIN_LITE;
|
|
log('SUCCESS', 'NOSTR_LOGIN_LITE initialized successfully');
|
|
document.getElementById('status').innerHTML = '✅ Ready - Click "Launch Login Modal" to test all login methods';
|
|
document.getElementById('status').className = 'status logged-in';
|
|
|
|
} catch (error) {
|
|
log('ERROR', `Initialization failed: ${error.message}`);
|
|
document.getElementById('status').innerHTML = '❌ Failed to initialize NOSTR_LOGIN_LITE';
|
|
document.getElementById('status').className = 'status logged-out';
|
|
}
|
|
}
|
|
|
|
// Launch the login modal
|
|
async function launchLoginModal() {
|
|
log('INFO', 'Launching NOSTR_LOGIN_LITE modal...');
|
|
document.getElementById('status').innerHTML = '🔄 Opening login modal...';
|
|
document.getElementById('status').className = 'status loading';
|
|
|
|
try {
|
|
// Launch the modal
|
|
await nlLite.launch('login');
|
|
log('SUCCESS', 'Login modal launched successfully');
|
|
|
|
} catch (error) {
|
|
log('ERROR', `Failed to launch modal: ${error.message}`);
|
|
document.getElementById('status').innerHTML = '❌ Failed to launch modal';
|
|
document.getElementById('status').className = 'status logged-out';
|
|
}
|
|
}
|
|
|
|
// Handle authentication events
|
|
function handleAuthEvent(event) {
|
|
const { type, pubkey, method, error } = event.detail;
|
|
|
|
log('INFO', `Auth event received: type=${type}, method=${method}`);
|
|
|
|
if (type === 'login' && pubkey) {
|
|
userPubkey = pubkey;
|
|
log('SUCCESS', `Login successful! Method: ${method}, Pubkey: ${pubkey}`);
|
|
|
|
document.getElementById('status').innerHTML = `✅ Logged in via ${method}!`;
|
|
document.getElementById('status').className = 'status logged-in';
|
|
|
|
// Show profile section
|
|
document.getElementById('login-section').style.display = 'none';
|
|
document.getElementById('profile-section').style.display = 'block';
|
|
|
|
// Load profile
|
|
loadUserProfile();
|
|
|
|
} else if (type === 'logout') {
|
|
log('INFO', 'User logged out');
|
|
userPubkey = null;
|
|
|
|
document.getElementById('status').innerHTML = '✅ Ready - Click "Launch Login Modal" to test all login methods';
|
|
document.getElementById('status').className = 'status logged-in';
|
|
|
|
// Show login section
|
|
document.getElementById('login-section').style.display = 'block';
|
|
document.getElementById('profile-section').style.display = 'none';
|
|
|
|
} else if (error) {
|
|
log('ERROR', `Authentication error: ${error}`);
|
|
document.getElementById('status').innerHTML = '❌ Authentication failed';
|
|
document.getElementById('status').className = 'status logged-out';
|
|
}
|
|
}
|
|
|
|
// Load user profile
|
|
async function loadUserProfile() {
|
|
if (!userPubkey) return;
|
|
|
|
log('INFO', `Loading profile for: ${userPubkey}`);
|
|
document.getElementById('profile-name').textContent = 'Loading profile...';
|
|
document.getElementById('profile-pubkey').textContent = userPubkey;
|
|
|
|
try {
|
|
// Simple WebSocket connection to get profile
|
|
const ws = new WebSocket(relayUrl);
|
|
|
|
ws.onopen = () => {
|
|
log('SUCCESS', 'WebSocket connected, requesting profile...');
|
|
const req = JSON.stringify([
|
|
'REQ',
|
|
'profile',
|
|
{
|
|
kinds: [0],
|
|
authors: [userPubkey],
|
|
limit: 1
|
|
}
|
|
]);
|
|
ws.send(req);
|
|
};
|
|
|
|
ws.onmessage = (event) => {
|
|
try {
|
|
const message = JSON.parse(event.data);
|
|
const [type, subscriptionId, eventData] = message;
|
|
|
|
if (type === 'EVENT' && eventData && eventData.kind === 0) {
|
|
log('SUCCESS', 'Profile event received');
|
|
const profile = JSON.parse(eventData.content);
|
|
displayProfile(profile);
|
|
ws.close();
|
|
} else if (type === 'EOSE') {
|
|
log('INFO', 'End of subscription');
|
|
ws.close();
|
|
}
|
|
} catch (parseError) {
|
|
log('ERROR', `Failed to parse WebSocket message: ${parseError.message}`);
|
|
}
|
|
};
|
|
|
|
ws.onerror = (error) => {
|
|
log('ERROR', `WebSocket error: ${error.message || 'Connection failed'}`);
|
|
document.getElementById('profile-name').textContent = 'Error loading profile';
|
|
};
|
|
|
|
// Timeout after 5 seconds
|
|
setTimeout(() => {
|
|
if (ws.readyState !== WebSocket.CLOSED) {
|
|
ws.close();
|
|
if (document.getElementById('profile-name').textContent === 'Loading profile...') {
|
|
document.getElementById('profile-name').textContent = 'Profile timeout';
|
|
document.getElementById('profile-about').textContent = 'Could not load profile from relay.';
|
|
log('WARNING', 'Profile request timed out');
|
|
}
|
|
}
|
|
}, 5000);
|
|
|
|
} catch (error) {
|
|
log('ERROR', `Profile loading failed: ${error.message}`);
|
|
document.getElementById('profile-name').textContent = 'Error loading profile';
|
|
document.getElementById('profile-about').textContent = error.message;
|
|
}
|
|
}
|
|
|
|
// Display profile data
|
|
function displayProfile(profile) {
|
|
const name = profile.name || profile.display_name || profile.displayName || 'Anonymous User';
|
|
const about = profile.about || '';
|
|
const picture = profile.picture || '';
|
|
|
|
document.getElementById('profile-name').textContent = name;
|
|
document.getElementById('profile-about').textContent = about;
|
|
|
|
if (picture) {
|
|
document.getElementById('profile-picture').src = picture;
|
|
}
|
|
|
|
log('SUCCESS', `Profile displayed: ${name}`);
|
|
}
|
|
|
|
// Logout function
|
|
async function logout() {
|
|
log('INFO', 'Logging out...');
|
|
try {
|
|
await nlLite.logout();
|
|
log('SUCCESS', 'Logged out successfully');
|
|
} catch (error) {
|
|
log('ERROR', `Logout failed: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
// Set up event listeners
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
// Button event listeners
|
|
document.getElementById('launch-modal').addEventListener('click', launchLoginModal);
|
|
document.getElementById('refresh-profile').addEventListener('click', loadUserProfile);
|
|
document.getElementById('logout').addEventListener('click', logout);
|
|
|
|
// Listen for authentication events
|
|
window.addEventListener('nlAuth', handleAuthEvent);
|
|
|
|
// Initialize the app
|
|
setTimeout(initializeApp, 100);
|
|
});
|
|
</script>
|
|
</body>
|
|
</html> |