/** * Unified authentication handler */ import { getNIP07Signer, signEventWithNIP07, getPublicKeyWithNIP07 } from '../auth/nip07-signer.js'; import { signEventWithNsec, getPublicKeyFromNsec } from '../auth/nsec-signer.js'; import { signEventWithAnonymous, generateAnonymousKey } from '../auth/anonymous-signer.js'; import { decryptPrivateKey } from '../security/key-management.js'; import { sessionManager, type AuthMethod } from '../auth/session-manager.js'; import { fetchRelayLists, fetchProfile } from '../user-data.js'; import { nostrClient } from './nostr-client.js'; import { relayManager } from './relay-manager.js'; import type { NostrEvent } from '../../types/nostr.js'; import { KIND } from '../../types/kind-lookup.js'; // Mute list and blocked relays management const muteList: Set = new Set(); const blockedRelays: Set = new Set(); /** * Authenticate with NIP-07 */ export async function authenticateWithNIP07(): Promise { const pubkey = await getPublicKeyWithNIP07(); sessionManager.setSession({ pubkey, method: 'nip07', signer: signEventWithNIP07, createdAt: Date.now() }, {}); // No metadata needed for NIP-07 // Fetch user relay lists and mute list await loadUserPreferences(pubkey); // Fetch and cache user's own profile (background-update if already cached) fetchProfile(pubkey).catch(() => { // Silently fail - profile fetch errors shouldn't break login }); return pubkey; } /** * Authenticate with nsec */ export async function authenticateWithNsec( ncryptsec: string, password: string ): Promise { // Decrypt the encrypted private key const nsec = await decryptPrivateKey(ncryptsec, password); // Derive public key from private key const pubkey = await getPublicKeyFromNsec(nsec); sessionManager.setSession({ pubkey, method: 'nsec', signer: async (event) => signEventWithNsec(event, ncryptsec, password), createdAt: Date.now() }); await loadUserPreferences(pubkey); // Fetch and cache user's own profile (background-update if already cached) fetchProfile(pubkey).catch(() => { // Silently fail - profile fetch errors shouldn't break login }); return pubkey; } /** * Authenticate as anonymous */ export async function authenticateAsAnonymous(password: string): Promise { const { pubkey, nsec } = await generateAnonymousKey(password); // Store the key for later use // In practice, we'd need to store the ncryptsec and decrypt when needed // For now, this is simplified sessionManager.setSession({ pubkey, method: 'anonymous', signer: async (event) => { // Simplified - would decrypt and sign return signEventWithAnonymous(event, pubkey, password); }, createdAt: Date.now() }); return pubkey; } /** * Load user preferences (relay lists, mute list, blocked relays) */ async function loadUserPreferences(pubkey: string): Promise { // Fetch relay lists and load into relay manager await relayManager.loadUserPreferences(pubkey); // Fetch mute list (kind 10000) const muteEvents = await nostrClient.fetchEvents( [{ kinds: [KIND.MUTE_LIST], authors: [pubkey], limit: 1 }], relayManager.getProfileReadRelays(), { useCache: true, cacheResults: true } ); if (muteEvents.length > 0) { const mutedPubkeys = muteEvents[0].tags .filter((t) => t[0] === 'p') .map((t) => t[1]) .filter(Boolean) as string[]; muteList.clear(); mutedPubkeys.forEach(pk => muteList.add(pk)); } // Fetch blocked relays (kind 10006) const blockedRelayEvents = await nostrClient.fetchEvents( [{ kinds: [KIND.BLOCKED_RELAYS], authors: [pubkey], limit: 1 }], relayManager.getProfileReadRelays(), { useCache: true, cacheResults: true } ); if (blockedRelayEvents.length > 0) { const blocked = blockedRelayEvents[0].tags .filter((t) => t[0] === 'relay') .map((t) => t[1]) .filter(Boolean) as string[]; blockedRelays.clear(); blocked.forEach(r => blockedRelays.add(r)); // Update relay manager with blocked relays relayManager.updateBlockedRelays(blockedRelays); } } /** * Sign HTTP auth (NIP-98) for authenticated HTTP requests * Returns Authorization header value: "Nostr " */ export async function signHttpAuth( url: string, method: string, description: string = '' ): Promise { const event = await sessionManager.signEvent({ kind: KIND.HTTP_AUTH, pubkey: sessionManager.getCurrentPubkey()!, created_at: Math.floor(Date.now() / 1000), tags: [ ['u', url], ['method', method] ], content: description }); // Base64 encode the event JSON and return as "Nostr " const eventJson = JSON.stringify(event); const base64 = btoa(eventJson); return `Nostr ${base64}`; } /** * Sign and publish event */ export async function signAndPublish( event: Omit, relays?: string[] ): Promise<{ success: string[]; failed: Array<{ relay: string; error: string }>; }> { const signed = await sessionManager.signEvent(event); return nostrClient.publish(signed, { relays }); } /** * Logout */ export function logout(): void { sessionManager.clearSession(); muteList.clear(); blockedRelays.clear(); relayManager.clearUserPreferences(); } /** * Get mute list */ export function getMuteList(): Set { return muteList; } /** * Get blocked relays */ export function getBlockedRelays(): Set { return blockedRelays; }