Fixed persistance issues

This commit is contained in:
Your Name
2025-09-20 15:33:14 -04:00
parent 966d9d0456
commit a7dceb1156
3 changed files with 289 additions and 19 deletions

View File

@@ -528,10 +528,21 @@ class FloatingTab {
} }
menu.style.top = tabRect.top + 'px'; menu.style.top = tabRect.top + 'px';
// Menu content // Menu content - use _getAuthState() as single source of truth
const userDisplay = this.userInfo?.pubkey ? const authState = this._getAuthState();
\`\${this.userInfo.pubkey.slice(0, 8)}...\${this.userInfo.pubkey.slice(-4)}\` : let userDisplay;
'Authenticated';
if (authState?.pubkey) {
// Use profile name if available, otherwise pubkey
if (this.userProfile?.name || this.userProfile?.display_name) {
const userName = this.userProfile.name || this.userProfile.display_name;
userDisplay = userName.length > 16 ? \`\${userName.slice(0, 16)}...\` : userName;
} else {
userDisplay = \`\${authState.pubkey.slice(0, 8)}...\${authState.pubkey.slice(-4)}\`;
}
} else {
userDisplay = 'Authenticated';
}
menu.innerHTML = \` menu.innerHTML = \`
<div style="margin-bottom: 8px; font-weight: bold; color: var(--nl-primary-color);">\${userDisplay}</div> <div style="margin-bottom: 8px; font-weight: bold; color: var(--nl-primary-color);">\${userDisplay}</div>
@@ -1163,7 +1174,11 @@ class NostrLite {
localStorage.removeItem('nl_current'); localStorage.removeItem('nl_current');
} }
// Dispatch logout event (AuthManager will clear its own data via WindowNostr listener) // Clear current authentication state directly from storage
// This works for ALL methods including extensions (fixes the bug)
clearAuthState();
// Dispatch logout event for UI updates
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
window.dispatchEvent(new CustomEvent('nlLogout', { window.dispatchEvent(new CustomEvent('nlLogout', {
detail: { timestamp: Date.now() } detail: { timestamp: Date.now() }
@@ -1796,12 +1811,12 @@ class WindowNostr {
console.log('WindowNostr: Captured authenticated extension:', this.authenticatedExtension?.constructor?.name); console.log('WindowNostr: Captured authenticated extension:', this.authenticatedExtension?.constructor?.name);
} }
// Save authentication state for persistence // Use global setAuthState function for unified persistence
try { try {
await this.authManager.saveAuthState(event.detail); setAuthState(event.detail, { isolateSession: this.nostrLite.options?.isolateSession });
console.log('WindowNostr: Auth state saved for persistence'); console.log('WindowNostr: Auth state saved via global setAuthState');
} catch (error) { } catch (error) {
console.error('WindowNostr: Failed to save auth state:', error); console.error('WindowNostr: Failed to save auth state via global setAuthState:', error);
} }
// EXTENSION-FIRST: Only reinstall facade for non-extension methods // EXTENSION-FIRST: Only reinstall facade for non-extension methods
@@ -2210,6 +2225,117 @@ function getAuthState() {
} }
} }
// ======================================
// Global Authentication State Management - Unified Persistence
// ======================================
// Global setAuthState function for unified persistence across all authentication methods
function setAuthState(authData, options = {}) {
try {
console.log('🔍 setAuthState: === GLOBAL AUTH STATE SAVE ===');
console.log('🔍 setAuthState: authData:', authData);
console.log('🔍 setAuthState: options:', options);
const storageKey = 'nostr_login_lite_auth';
// Determine which storage to use based on isolateSession option
const storage = options.isolateSession ? sessionStorage : localStorage;
const storageType = options.isolateSession ? 'sessionStorage' : 'localStorage';
console.log('🔍 setAuthState: Using', storageType, 'for persistence');
// Create auth state object
const authState = {
method: authData.method,
timestamp: Date.now(),
pubkey: authData.pubkey
};
// Add method-specific data (but no secrets for extension method)
switch (authData.method) {
case 'extension':
// For extensions, only store verification data - no secrets
authState.extensionVerification = {
constructor: authData.extension?.constructor?.name,
hasGetPublicKey: typeof authData.extension?.getPublicKey === 'function',
hasSignEvent: typeof authData.extension?.signEvent === 'function'
};
console.log('🔍 setAuthState: Extension method - storing verification data only');
break;
case 'local':
// For local keys, store the secret (will be encrypted by AuthManager if needed)
if (authData.secret) {
authState.secret = authData.secret;
console.log('🔍 setAuthState: Local method - storing secret key');
}
break;
case 'nip46':
// For NIP-46, store connection parameters
if (authData.signer) {
authState.nip46 = {
remotePubkey: authData.signer.remotePubkey,
relays: authData.signer.relays,
// Don't store secret - user will need to reconnect
};
console.log('🔍 setAuthState: NIP-46 method - storing connection parameters');
}
break;
case 'readonly':
// Read-only mode has no additional data to store
console.log('🔍 setAuthState: Read-only method - storing basic auth state');
break;
default:
console.warn('🔍 setAuthState: Unknown auth method:', authData.method);
break;
}
// Store the auth state
storage.setItem(storageKey, JSON.stringify(authState));
console.log('🔍 setAuthState: ✅ Auth state saved successfully');
console.log('🔍 setAuthState: Final auth state:', authState);
return authState;
} catch (error) {
console.error('🔍 setAuthState: ❌ Error saving auth state:', error);
throw error;
}
}
// ======================================
// Global Authentication State Clearing
// ======================================
// Global clearAuthState function for unified auth state clearing
function clearAuthState() {
try {
console.log('🔍 clearAuthState: === GLOBAL AUTH STATE CLEAR ===');
const storageKey = 'nostr_login_lite_auth';
// Clear from both storage types to ensure complete cleanup
if (typeof sessionStorage !== 'undefined') {
sessionStorage.removeItem(storageKey);
sessionStorage.removeItem('nostr_session_key');
console.log('🔍 clearAuthState: ✅ Cleared auth state from sessionStorage');
}
if (typeof localStorage !== 'undefined') {
localStorage.removeItem(storageKey);
console.log('🔍 clearAuthState: ✅ Cleared auth state from localStorage');
}
console.log('🔍 clearAuthState: ✅ All auth state cleared successfully');
} catch (error) {
console.error('🔍 clearAuthState: ❌ Error clearing auth state:', error);
}
}
// Initialize and export // Initialize and export
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
@@ -2238,6 +2364,8 @@ if (typeof window !== 'undefined') {
// GLOBAL AUTHENTICATION STATE API - Single Source of Truth // GLOBAL AUTHENTICATION STATE API - Single Source of Truth
getAuthState: getAuthState, getAuthState: getAuthState,
setAuthState: setAuthState,
clearAuthState: clearAuthState,
// Expose for debugging // Expose for debugging
_extensionBridge: nostrLite.extensionBridge, _extensionBridge: nostrLite.extensionBridge,

View File

@@ -8,7 +8,7 @@
* Two-file architecture: * Two-file architecture:
* 1. Load nostr.bundle.js (official nostr-tools bundle) * 1. Load nostr.bundle.js (official nostr-tools bundle)
* 2. Load nostr-lite.js (this file - NOSTR_LOGIN_LITE library with CSS-only themes) * 2. Load nostr-lite.js (this file - NOSTR_LOGIN_LITE library with CSS-only themes)
* Generated on: 2025-09-20T14:23:53.897Z * Generated on: 2025-09-20T19:25:01.143Z
*/ */
// Verify dependencies are loaded // Verify dependencies are loaded
@@ -1382,11 +1382,18 @@ class Modal {
if (method === 'extension') { if (method === 'extension') {
console.log('Modal: Extension method - NOT installing facade, leaving window.nostr as extension'); console.log('Modal: Extension method - NOT installing facade, leaving window.nostr as extension');
// Save extension authentication state using global setAuthState function
if (typeof window.setAuthState === 'function') {
console.log('Modal: Saving extension auth state to storage');
window.setAuthState({ method, ...options }, { isolateSession: this.options?.isolateSession });
}
// Emit auth method selection directly for extension // Emit auth method selection directly for extension
const event = new CustomEvent('nlMethodSelected', { const event = new CustomEvent('nlMethodSelected', {
detail: { method, ...options } detail: { method, ...options }
}); });
window.dispatchEvent(event); window.dispatchEvent(event);
this.close(); this.close();
return; return;
} }
@@ -2495,10 +2502,21 @@ class FloatingTab {
} }
menu.style.top = tabRect.top + 'px'; menu.style.top = tabRect.top + 'px';
// Menu content // Menu content - use _getAuthState() as single source of truth
const userDisplay = this.userInfo?.pubkey ? const authState = this._getAuthState();
`${this.userInfo.pubkey.slice(0, 8)}...${this.userInfo.pubkey.slice(-4)}` : let userDisplay;
'Authenticated';
if (authState?.pubkey) {
// Use profile name if available, otherwise pubkey
if (this.userProfile?.name || this.userProfile?.display_name) {
const userName = this.userProfile.name || this.userProfile.display_name;
userDisplay = userName.length > 16 ? `${userName.slice(0, 16)}...` : userName;
} else {
userDisplay = `${authState.pubkey.slice(0, 8)}...${authState.pubkey.slice(-4)}`;
}
} else {
userDisplay = 'Authenticated';
}
menu.innerHTML = ` menu.innerHTML = `
<div style="margin-bottom: 8px; font-weight: bold; color: var(--nl-primary-color);">${userDisplay}</div> <div style="margin-bottom: 8px; font-weight: bold; color: var(--nl-primary-color);">${userDisplay}</div>
@@ -3130,7 +3148,11 @@ class NostrLite {
localStorage.removeItem('nl_current'); localStorage.removeItem('nl_current');
} }
// Dispatch logout event (AuthManager will clear its own data via WindowNostr listener) // Clear current authentication state directly from storage
// This works for ALL methods including extensions (fixes the bug)
clearAuthState();
// Dispatch logout event for UI updates
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
window.dispatchEvent(new CustomEvent('nlLogout', { window.dispatchEvent(new CustomEvent('nlLogout', {
detail: { timestamp: Date.now() } detail: { timestamp: Date.now() }
@@ -3763,12 +3785,12 @@ class WindowNostr {
console.log('WindowNostr: Captured authenticated extension:', this.authenticatedExtension?.constructor?.name); console.log('WindowNostr: Captured authenticated extension:', this.authenticatedExtension?.constructor?.name);
} }
// Save authentication state for persistence // Use global setAuthState function for unified persistence
try { try {
await this.authManager.saveAuthState(event.detail); setAuthState(event.detail, { isolateSession: this.nostrLite.options?.isolateSession });
console.log('WindowNostr: Auth state saved for persistence'); console.log('WindowNostr: Auth state saved via global setAuthState');
} catch (error) { } catch (error) {
console.error('WindowNostr: Failed to save auth state:', error); console.error('WindowNostr: Failed to save auth state via global setAuthState:', error);
} }
// EXTENSION-FIRST: Only reinstall facade for non-extension methods // EXTENSION-FIRST: Only reinstall facade for non-extension methods
@@ -4177,6 +4199,117 @@ function getAuthState() {
} }
} }
// ======================================
// Global Authentication State Management - Unified Persistence
// ======================================
// Global setAuthState function for unified persistence across all authentication methods
function setAuthState(authData, options = {}) {
try {
console.log('🔍 setAuthState: === GLOBAL AUTH STATE SAVE ===');
console.log('🔍 setAuthState: authData:', authData);
console.log('🔍 setAuthState: options:', options);
const storageKey = 'nostr_login_lite_auth';
// Determine which storage to use based on isolateSession option
const storage = options.isolateSession ? sessionStorage : localStorage;
const storageType = options.isolateSession ? 'sessionStorage' : 'localStorage';
console.log('🔍 setAuthState: Using', storageType, 'for persistence');
// Create auth state object
const authState = {
method: authData.method,
timestamp: Date.now(),
pubkey: authData.pubkey
};
// Add method-specific data (but no secrets for extension method)
switch (authData.method) {
case 'extension':
// For extensions, only store verification data - no secrets
authState.extensionVerification = {
constructor: authData.extension?.constructor?.name,
hasGetPublicKey: typeof authData.extension?.getPublicKey === 'function',
hasSignEvent: typeof authData.extension?.signEvent === 'function'
};
console.log('🔍 setAuthState: Extension method - storing verification data only');
break;
case 'local':
// For local keys, store the secret (will be encrypted by AuthManager if needed)
if (authData.secret) {
authState.secret = authData.secret;
console.log('🔍 setAuthState: Local method - storing secret key');
}
break;
case 'nip46':
// For NIP-46, store connection parameters
if (authData.signer) {
authState.nip46 = {
remotePubkey: authData.signer.remotePubkey,
relays: authData.signer.relays,
// Don't store secret - user will need to reconnect
};
console.log('🔍 setAuthState: NIP-46 method - storing connection parameters');
}
break;
case 'readonly':
// Read-only mode has no additional data to store
console.log('🔍 setAuthState: Read-only method - storing basic auth state');
break;
default:
console.warn('🔍 setAuthState: Unknown auth method:', authData.method);
break;
}
// Store the auth state
storage.setItem(storageKey, JSON.stringify(authState));
console.log('🔍 setAuthState: ✅ Auth state saved successfully');
console.log('🔍 setAuthState: Final auth state:', authState);
return authState;
} catch (error) {
console.error('🔍 setAuthState: ❌ Error saving auth state:', error);
throw error;
}
}
// ======================================
// Global Authentication State Clearing
// ======================================
// Global clearAuthState function for unified auth state clearing
function clearAuthState() {
try {
console.log('🔍 clearAuthState: === GLOBAL AUTH STATE CLEAR ===');
const storageKey = 'nostr_login_lite_auth';
// Clear from both storage types to ensure complete cleanup
if (typeof sessionStorage !== 'undefined') {
sessionStorage.removeItem(storageKey);
sessionStorage.removeItem('nostr_session_key');
console.log('🔍 clearAuthState: ✅ Cleared auth state from sessionStorage');
}
if (typeof localStorage !== 'undefined') {
localStorage.removeItem(storageKey);
console.log('🔍 clearAuthState: ✅ Cleared auth state from localStorage');
}
console.log('🔍 clearAuthState: ✅ All auth state cleared successfully');
} catch (error) {
console.error('🔍 clearAuthState: ❌ Error clearing auth state:', error);
}
}
// Initialize and export // Initialize and export
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
@@ -4205,6 +4338,8 @@ if (typeof window !== 'undefined') {
// GLOBAL AUTHENTICATION STATE API - Single Source of Truth // GLOBAL AUTHENTICATION STATE API - Single Source of Truth
getAuthState: getAuthState, getAuthState: getAuthState,
setAuthState: setAuthState,
clearAuthState: clearAuthState,
// Expose for debugging // Expose for debugging
_extensionBridge: nostrLite.extensionBridge, _extensionBridge: nostrLite.extensionBridge,

View File

@@ -1085,11 +1085,18 @@ class Modal {
if (method === 'extension') { if (method === 'extension') {
console.log('Modal: Extension method - NOT installing facade, leaving window.nostr as extension'); console.log('Modal: Extension method - NOT installing facade, leaving window.nostr as extension');
// Save extension authentication state using global setAuthState function
if (typeof window.setAuthState === 'function') {
console.log('Modal: Saving extension auth state to storage');
window.setAuthState({ method, ...options }, { isolateSession: this.options?.isolateSession });
}
// Emit auth method selection directly for extension // Emit auth method selection directly for extension
const event = new CustomEvent('nlMethodSelected', { const event = new CustomEvent('nlMethodSelected', {
detail: { method, ...options } detail: { method, ...options }
}); });
window.dispatchEvent(event); window.dispatchEvent(event);
this.close(); this.close();
return; return;
} }