You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
204 lines
5.4 KiB
204 lines
5.4 KiB
/** |
|
* 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<string> = new Set(); |
|
const blockedRelays: Set<string> = new Set(); |
|
|
|
/** |
|
* Authenticate with NIP-07 |
|
*/ |
|
export async function authenticateWithNIP07(): Promise<string> { |
|
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<string> { |
|
// 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<string> { |
|
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<void> { |
|
// 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 <base64-encoded-event>" |
|
*/ |
|
export async function signHttpAuth( |
|
url: string, |
|
method: string, |
|
description: string = '' |
|
): Promise<string> { |
|
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 <base64>" |
|
const eventJson = JSON.stringify(event); |
|
const base64 = btoa(eventJson); |
|
return `Nostr ${base64}`; |
|
} |
|
|
|
/** |
|
* Sign and publish event |
|
*/ |
|
export async function signAndPublish( |
|
event: Omit<NostrEvent, 'sig' | 'id'>, |
|
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<string> { |
|
return muteList; |
|
} |
|
|
|
/** |
|
* Get blocked relays |
|
*/ |
|
export function getBlockedRelays(): Set<string> { |
|
return blockedRelays; |
|
}
|
|
|