Fixed persistance issues
This commit is contained in:
146
lite/build.js
146
lite/build.js
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user