ns-auth-sdk 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -0
- package/dist/index.cjs +6 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +1 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +6 -4
- package/dist/index.mjs.map +1 -1
- package/dist/{utils-CxLeFzNn.cjs → utils-BUM1trwg.cjs} +13 -5
- package/dist/utils-BUM1trwg.cjs.map +1 -0
- package/dist/utils-BV-7ST9t.cjs +3 -0
- package/dist/{utils-BVvtCpMQ.mjs → utils-CIdi100K.mjs} +1 -1
- package/dist/{utils-CV_lg5Ir.mjs → utils-DrZ-SwZX.mjs} +13 -5
- package/dist/utils-DrZ-SwZX.mjs.map +1 -0
- package/package.json +3 -4
- package/dist/utils-BR2TqhEA.cjs +0 -3
- package/dist/utils-CV_lg5Ir.mjs.map +0 -1
- package/dist/utils-CxLeFzNn.cjs.map +0 -1
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/services/auth.service.ts","../src/utils/validation.ts","../src/services/relay.service.ts","../src/components/auth/LoginButton.tsx","../src/components/auth/RegistrationFlow.tsx","../src/utils/sanitize.ts","../src/components/membership/BarcodeScanner.tsx","../src/components/membership/MembershipPage.tsx","../src/components/profile/ProfilePage.tsx","../src/hooks/useAuth.ts","../src/store/authStore.ts"],"sourcesContent":["import { NosskeyManager, type NostrKeyInfo, type NostrEvent, type PasskeyCreationOptions } from '../utils';\nimport type { AuthServiceConfig } from '../types/auth';\n\n/**\n * Service wrapper around NosskeyManager\n * Handles WebAuthn/Passkey integration with Nostr\n */\nexport class AuthService {\n private manager: NosskeyManager | null = null;\n private config: Required<AuthServiceConfig>;\n\n constructor(config: AuthServiceConfig = {}) {\n this.config = {\n rpId: config.rpId || (typeof window !== 'undefined' ? window.location.hostname.replace(/^www\\./, '') : 'localhost'),\n rpName: config.rpName || this.getDefaultRpName(),\n storageKey: config.storageKey || 'nsauth_keyinfo',\n cacheTimeoutMs: config.cacheTimeoutMs || 30 * 60 * 1000,\n };\n }\n\n private getDefaultRpName(): string {\n if (typeof window === 'undefined') return 'localhost';\n const hostname = window.location.hostname;\n if (hostname.includes('nosskey.app')) return 'nosskey.app';\n return hostname.replace(/^www\\./, '');\n }\n\n /**\n * Initialize the NosskeyManager instance\n */\n private getManager(): NosskeyManager {\n if (!this.manager) {\n this.manager = new NosskeyManager({\n cacheOptions: {\n enabled: true,\n timeoutMs: this.config.cacheTimeoutMs,\n },\n storageOptions: {\n enabled: true,\n storageKey: this.config.storageKey,\n },\n });\n }\n return this.manager;\n }\n\n /**\n * Create a new passkey\n * Uses platform authenticator only (Touch ID, Face ID, Windows Hello)\n */\n async createPasskey(username?: string): Promise<Uint8Array> {\n const manager = this.getManager();\n \n const rpId = this.config.rpId === 'localhost' ? 'localhost' : this.config.rpId;\n const rpName = this.config.rpName;\n \n const trimmedUsername = username?.trim();\n const uniqueUsername = trimmedUsername \n ? trimmedUsername \n : `user-${Date.now()}@example.com`;\n \n const options: PasskeyCreationOptions = {\n rp: {\n id: rpId,\n name: rpName,\n },\n user: {\n name: uniqueUsername,\n displayName: trimmedUsername || 'User',\n },\n authenticatorSelection: {\n authenticatorAttachment: 'platform',\n residentKey: 'preferred',\n userVerification: 'preferred',\n },\n extensions: {\n prf: {},\n },\n };\n \n return await manager.createPasskey(options);\n }\n\n /**\n * Create a new Nostr key from a credential ID\n */\n async createNostrKey(credentialId?: Uint8Array): Promise<NostrKeyInfo> {\n const manager = this.getManager();\n return await manager.createNostrKey(credentialId);\n }\n\n /**\n * Get the current public key\n */\n async getPublicKey(): Promise<string> {\n const manager = this.getManager();\n return await manager.getPublicKey();\n }\n\n /**\n * Sign a Nostr event\n */\n async signEvent(event: NostrEvent): Promise<NostrEvent> {\n const manager = this.getManager();\n return await manager.signEvent(event);\n }\n\n /**\n * Get current key info\n */\n getCurrentKeyInfo(): NostrKeyInfo | null {\n const manager = this.getManager();\n return manager.getCurrentKeyInfo();\n }\n\n /**\n * Set current key info\n */\n setCurrentKeyInfo(keyInfo: NostrKeyInfo): void {\n const manager = this.getManager();\n manager.setCurrentKeyInfo(keyInfo);\n }\n\n /**\n * Check if key info exists\n */\n hasKeyInfo(): boolean {\n const manager = this.getManager();\n return manager.hasKeyInfo();\n }\n\n /**\n * Clear stored key info\n */\n clearStoredKeyInfo(): void {\n const manager = this.getManager();\n manager.clearStoredKeyInfo();\n }\n\n /**\n * Check if PRF is supported\n */\n async isPrfSupported(): Promise<boolean> {\n const { isPrfSupported } = await import('../utils');\n return await isPrfSupported();\n }\n}\n\n","export const MAX_ROLE_LENGTH = 100;\nconst ROLE_PATTERN = /^[a-zA-Z0-9\\s\\-_]+$/;\n\nexport const isValidRoleTag = (role: string): boolean => {\n const trimmed = role.trim();\n return (\n trimmed.length > 0 &&\n trimmed.length <= MAX_ROLE_LENGTH &&\n ROLE_PATTERN.test(trimmed)\n );\n};\n\nexport const isValidPubkey = (pubkey: string): boolean =>\n pubkey.length === 64 && /^[0-9a-fA-F]+$/.test(pubkey);\n\nexport const isValidHttpUrl = (url: string): boolean => {\n try {\n const parsed = new URL(url);\n return ['http:', 'https:'].includes(parsed.protocol);\n } catch {\n return false;\n }\n};\n\nexport const isValidRelayUrl = (url: string): boolean => {\n try {\n const parsed = new URL(url);\n return ['ws:', 'wss:'].includes(parsed.protocol) && parsed.hostname.length > 0;\n } catch {\n return false;\n }\n};\n","import type { EventStore } from 'applesauce-core';\nimport type { NostrEvent } from '../types/nostr';\nimport type { ProfileMetadata, FollowEntry } from '../types/nostr';\nimport type { RelayServiceConfig } from '../types/auth';\nimport { isValidPubkey, isValidRelayUrl, isValidRoleTag } from '../utils/validation';\n\n/**\n * Service for communicating with Nostr relays using applesauce-core\n */\nexport class RelayService {\n private eventStore: EventStore | null = null;\n private relayUrls: string[];\n private defaultRelays = ['wss://relay.damus.io'];\n private readonly maxProfileContentSize = 10000;\n private readonly minProfileQueryIntervalMs = 300;\n private readonly minPublishIntervalMs = 750;\n private readonly lastActionAt = new Map<string, number>();\n\n constructor(config: RelayServiceConfig = {}) {\n this.relayUrls = this.validateRelayUrls(config.relayUrls ?? this.defaultRelays);\n }\n\n /**\n * Initialize with applesauce EventStore\n */\n initialize(eventStore: EventStore): void {\n this.eventStore = eventStore;\n // Set default relays if EventStore has a method for it\n if (eventStore && 'setRelays' in eventStore && typeof eventStore.setRelays === 'function') {\n (eventStore as any).setRelays(this.relayUrls);\n }\n }\n\n /**\n * Set relay URLs\n */\n setRelays(urls: string[]): void {\n this.relayUrls = this.validateRelayUrls(urls);\n if (this.eventStore && 'setRelays' in this.eventStore && typeof this.eventStore.setRelays === 'function') {\n (this.eventStore as any).setRelays(this.relayUrls);\n }\n }\n\n /**\n * Get current relay URLs\n */\n getRelays(): string[] {\n return [...this.relayUrls];\n }\n\n /**\n * Publish an event to relays\n */\n async publishEvent(event: NostrEvent, timeoutMs = 1000): Promise<boolean> {\n if (!this.eventStore) {\n throw new Error('RelayService not initialized. Call initialize() with an EventStore instance.');\n }\n\n await this.enforceRateLimit('publish', this.minPublishIntervalMs);\n return new Promise((resolve, reject) => {\n if (this.relayUrls.length === 0) {\n reject(new Error('No relays configured'));\n return;\n }\n\n // Use EventStore's publish method\n // Note: This is a simplified implementation - actual applesauce API may differ\n const eventStore = this.eventStore as any;\n if (!eventStore || typeof eventStore.publish !== 'function') {\n reject(new Error('EventStore does not support publish method'));\n return;\n }\n const subscription = eventStore.publish(event).subscribe({\n next: (response: any) => {\n if (response?.type === 'OK') {\n subscription.unsubscribe();\n resolve(true);\n }\n },\n error: (error: Error) => {\n subscription.unsubscribe();\n reject(error);\n },\n });\n\n // Timeout fallback\n setTimeout(() => {\n subscription.unsubscribe();\n resolve(false);\n }, timeoutMs);\n });\n }\n\n /**\n * Fetch a profile (Kind 0 event)\n */\n async fetchProfile(pubkey: string): Promise<ProfileMetadata | null> {\n if (!this.eventStore) {\n throw new Error('RelayService not initialized. Call initialize() with an EventStore instance.');\n }\n\n await this.enforceRateLimit('fetch-profile', this.minProfileQueryIntervalMs);\n return new Promise((resolve) => {\n const filter = {\n kinds: [0],\n authors: [pubkey],\n limit: 1,\n };\n\n const eventStore = this.eventStore as any;\n if (!eventStore || typeof eventStore.query !== 'function') {\n resolve(null);\n return;\n }\n const subscription = eventStore.query(filter).subscribe({\n next: (packet: any) => {\n if (packet?.event && packet.event.kind === 0) {\n const metadata = this.parseProfileMetadata(packet.event.content);\n if (metadata) {\n subscription.unsubscribe();\n resolve(metadata);\n return;\n }\n console.error('Failed to parse profile metadata');\n }\n },\n complete: () => {\n subscription.unsubscribe();\n resolve(null);\n },\n error: (error: Error) => {\n console.error('Error fetching profile:', error);\n subscription.unsubscribe();\n resolve(null);\n },\n });\n\n // Timeout\n setTimeout(() => {\n subscription.unsubscribe();\n resolve(null);\n }, 5000);\n });\n }\n\n /**\n * Fetch role tag from profile event (Kind 0)\n */\n async fetchProfileRoleTag(pubkey: string): Promise<string | null> {\n if (!this.eventStore) {\n throw new Error('RelayService not initialized. Call initialize() with an EventStore instance.');\n }\n\n await this.enforceRateLimit('fetch-role-tag', this.minProfileQueryIntervalMs);\n return new Promise((resolve) => {\n const filter = {\n kinds: [0],\n authors: [pubkey],\n limit: 1,\n };\n\n const eventStore = this.eventStore as any;\n if (!eventStore || typeof eventStore.query !== 'function') {\n resolve(null);\n return;\n }\n const subscription = eventStore.query(filter).subscribe({\n next: (packet: any) => {\n if (packet?.event && packet.event.kind === 0) {\n const tags = packet.event.tags || [];\n for (const tag of tags) {\n if (tag[0] === 'role' && tag[1]) {\n const candidate = tag[1].trim();\n if (isValidRoleTag(candidate)) {\n subscription.unsubscribe();\n resolve(candidate);\n return;\n }\n }\n }\n subscription.unsubscribe();\n resolve(null);\n }\n },\n complete: () => {\n subscription.unsubscribe();\n resolve(null);\n },\n error: (error: Error) => {\n console.error('Error fetching profile role tag:', error);\n subscription.unsubscribe();\n resolve(null);\n },\n });\n\n setTimeout(() => {\n subscription.unsubscribe();\n resolve(null);\n }, 5000);\n });\n }\n\n /**\n * Fetch a follow list (Kind 3 event)\n */\n async fetchFollowList(pubkey: string): Promise<FollowEntry[]> {\n if (!this.eventStore) {\n throw new Error('RelayService not initialized. Call initialize() with an EventStore instance.');\n }\n\n await this.enforceRateLimit('fetch-follow-list', this.minProfileQueryIntervalMs);\n return new Promise((resolve) => {\n const filter = {\n kinds: [3],\n authors: [pubkey],\n limit: 1,\n };\n\n const eventStore = this.eventStore as any;\n if (!eventStore || typeof eventStore.query !== 'function') {\n resolve([]);\n return;\n }\n const subscription = eventStore.query(filter).subscribe({\n next: (packet: any) => {\n if (packet?.event && packet.event.kind === 3) {\n const followList: FollowEntry[] = [];\n const tags = packet.event.tags || [];\n\n for (const tag of tags) {\n if (tag[0] === 'p' && tag[1]) {\n followList.push({\n pubkey: tag[1],\n relay: tag[2] || undefined,\n petname: tag[3] || undefined,\n });\n }\n }\n\n subscription.unsubscribe();\n resolve(followList);\n }\n },\n complete: () => {\n subscription.unsubscribe();\n resolve([]);\n },\n error: (error: Error) => {\n console.error('Error fetching follow list:', error);\n subscription.unsubscribe();\n resolve([]);\n },\n });\n\n setTimeout(() => {\n subscription.unsubscribe();\n resolve([]);\n }, 10000);\n });\n }\n\n /**\n * Fetch multiple profiles in batch\n */\n async fetchMultipleProfiles(pubkeys: string[]): Promise<Map<string, ProfileMetadata>> {\n if (!this.eventStore) {\n throw new Error('RelayService not initialized. Call initialize() with an EventStore instance.');\n }\n\n if (pubkeys.length === 0) {\n return new Map();\n }\n\n await this.enforceRateLimit('fetch-multiple-profiles', this.minProfileQueryIntervalMs);\n return new Promise((resolve) => {\n const profiles = new Map<string, ProfileMetadata>();\n const filter = {\n kinds: [0],\n authors: pubkeys,\n };\n\n const eventStore = this.eventStore as any;\n if (!eventStore || typeof eventStore.query !== 'function') {\n resolve(new Map());\n return;\n }\n const subscription = eventStore.query(filter).subscribe({\n next: (packet: any) => {\n if (packet?.event && packet.event.kind === 0 && packet.event.pubkey) {\n const metadata = this.parseProfileMetadata(packet.event.content);\n if (metadata) {\n profiles.set(packet.event.pubkey, metadata);\n } else {\n console.error('Failed to parse profile metadata');\n }\n }\n },\n complete: () => {\n subscription.unsubscribe();\n resolve(profiles);\n },\n error: (error: Error) => {\n console.error('Error fetching profiles:', error);\n subscription.unsubscribe();\n resolve(profiles);\n },\n });\n\n setTimeout(() => {\n subscription.unsubscribe();\n resolve(profiles);\n }, 1000);\n });\n }\n\n /**\n * Query kind 0 events (profiles) by pubkey\n * If pubkeys array is empty, fetches recent kind 0 events\n */\n async queryProfiles(pubkeys: string[] = [], limit = 100): Promise<Map<string, ProfileMetadata>> {\n if (!this.eventStore) {\n throw new Error('RelayService not initialized. Call initialize() with an EventStore instance.');\n }\n\n await this.enforceRateLimit('query-profiles', this.minProfileQueryIntervalMs);\n return new Promise((resolve) => {\n const profiles = new Map<string, { metadata: ProfileMetadata; timestamp: number }>();\n const filter: any = {\n kinds: [0],\n limit,\n };\n\n if (pubkeys.length > 0) {\n filter.authors = pubkeys;\n }\n\n const eventStore = this.eventStore as any;\n if (!eventStore || typeof eventStore.query !== 'function') {\n resolve(new Map());\n return;\n }\n const subscription = eventStore.query(filter).subscribe({\n next: (packet: any) => {\n if (packet?.event && packet.event.kind === 0 && packet.event.pubkey) {\n const metadata = this.parseProfileMetadata(packet.event.content);\n if (metadata) {\n const timestamp = packet.event.created_at || 0;\n const existing = profiles.get(packet.event.pubkey);\n if (!existing || timestamp > existing.timestamp) {\n profiles.set(packet.event.pubkey, { metadata, timestamp });\n }\n } else {\n console.error('Failed to parse profile metadata');\n }\n }\n },\n complete: () => {\n subscription.unsubscribe();\n const result = new Map<string, ProfileMetadata>();\n profiles.forEach((value, pubkey) => {\n result.set(pubkey, value.metadata);\n });\n resolve(result);\n },\n error: (error: Error) => {\n console.error('Error querying profiles:', error);\n subscription.unsubscribe();\n const result = new Map<string, ProfileMetadata>();\n profiles.forEach((value, pubkey) => {\n result.set(pubkey, value.metadata);\n });\n resolve(result);\n },\n });\n\n setTimeout(() => {\n subscription.unsubscribe();\n const result = new Map<string, ProfileMetadata>();\n profiles.forEach((value, pubkey) => {\n result.set(pubkey, value.metadata);\n });\n resolve(result);\n }, 10000);\n });\n }\n\n /**\n * Publish or update a kind 3 event (follow list/contacts)\n */\n async publishFollowList(\n pubkey: string,\n followList: FollowEntry[],\n signEvent: (event: NostrEvent) => Promise<NostrEvent>\n ): Promise<boolean> {\n const tags: string[][] = followList.map((entry) => {\n if (!isValidPubkey(entry.pubkey)) {\n throw new Error('Invalid pubkey format for follow list entry.');\n }\n if (entry.relay && !isValidRelayUrl(entry.relay)) {\n throw new Error('Invalid relay URL format for follow list entry.');\n }\n const tag: string[] = ['p', entry.pubkey];\n if (entry.relay) {\n tag.push(entry.relay);\n }\n if (entry.petname) {\n tag.push(entry.petname);\n }\n return tag;\n });\n\n const event: NostrEvent = {\n kind: 3,\n content: '',\n created_at: Math.floor(Date.now() / 1000),\n tags,\n };\n\n const signedEvent = await signEvent(event);\n return await this.publishEvent(signedEvent);\n }\n\n private validateRelayUrls(urls: string[]): string[] {\n if (urls.length === 0) {\n return [];\n }\n const validUrls = urls.filter((url) => isValidRelayUrl(url));\n if (validUrls.length !== urls.length) {\n throw new Error('Invalid relay URL format');\n }\n return validUrls;\n }\n\n private parseProfileMetadata(content: string): ProfileMetadata | null {\n if (content.length > this.maxProfileContentSize) {\n return null;\n }\n try {\n const parsed = JSON.parse(content);\n if (!parsed || typeof parsed !== 'object') {\n return null;\n }\n const metadata: ProfileMetadata = {};\n if (typeof (parsed as ProfileMetadata).name === 'string') {\n metadata.name = (parsed as ProfileMetadata).name;\n }\n if (typeof (parsed as ProfileMetadata).display_name === 'string') {\n metadata.display_name = (parsed as ProfileMetadata).display_name;\n }\n if (typeof (parsed as ProfileMetadata).about === 'string') {\n metadata.about = (parsed as ProfileMetadata).about;\n }\n if (typeof (parsed as ProfileMetadata).picture === 'string') {\n metadata.picture = (parsed as ProfileMetadata).picture;\n }\n if (typeof (parsed as ProfileMetadata).website === 'string') {\n metadata.website = (parsed as ProfileMetadata).website;\n }\n return metadata;\n } catch (error) {\n console.error('Failed to parse profile metadata:', error);\n return null;\n }\n }\n\n private async enforceRateLimit(action: string, minIntervalMs: number): Promise<void> {\n const lastAt = this.lastActionAt.get(action) ?? 0;\n const now = Date.now();\n const waitMs = minIntervalMs - (now - lastAt);\n if (waitMs > 0) {\n await new Promise((resolve) => setTimeout(resolve, waitMs));\n }\n this.lastActionAt.set(action, Date.now());\n }\n}\n\n","import { useState } from 'react';\nimport type { AuthService } from '../../services/auth.service';\nimport type { AuthState } from '../../types/auth';\nimport './Auth.css';\n\ninterface LoginButtonProps {\n authService: AuthService;\n setAuthenticated: AuthState['setAuthenticated'];\n setLoginError: AuthState['setLoginError'];\n onSuccess?: () => void;\n}\n\nexport function LoginButton({\n authService,\n setAuthenticated,\n setLoginError,\n onSuccess,\n}: LoginButtonProps) {\n const [isLoading, setIsLoading] = useState(false);\n\n const handleLogin = async () => {\n setIsLoading(true);\n setLoginError(null);\n\n try {\n if (!authService.hasKeyInfo()) {\n throw new Error('No account found. Please register first.');\n }\n\n const keyInfo = authService.getCurrentKeyInfo();\n if (!keyInfo) {\n throw new Error('Failed to load account information.');\n }\n\n await authService.getPublicKey();\n setAuthenticated(keyInfo);\n\n if (onSuccess) {\n onSuccess();\n }\n } catch (err) {\n console.error('Login error:', err);\n const errorMessage = err instanceof Error ? err.message : 'Failed to login';\n setLoginError(errorMessage);\n setIsLoading(false);\n }\n };\n\n return (\n <div className=\"login-button-container\">\n <button\n className=\"auth-button secondary\"\n onClick={handleLogin}\n disabled={isLoading}\n >\n {isLoading ? 'Logging in...' : 'Login'}\n </button>\n </div>\n );\n}\n\n","import { useState } from 'react';\nimport type { AuthService } from '../../services/auth.service';\nimport type { AuthState } from '../../types/auth';\nimport './Auth.css';\n\ninterface RegistrationFlowProps {\n authService: AuthService;\n setAuthenticated: AuthState['setAuthenticated'];\n onSuccess?: () => void;\n}\n\nexport function RegistrationFlow({\n authService,\n setAuthenticated,\n onSuccess,\n}: RegistrationFlowProps) {\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [step, setStep] = useState<'info' | 'creating' | 'success'>('info');\n const [username, setUsername] = useState('');\n const maxUsernameLength = 100;\n\n const handleRegister = async () => {\n setIsLoading(true);\n setError(null);\n setStep('creating');\n\n try {\n authService.clearStoredKeyInfo();\n\n let credentialId: Uint8Array;\n const passkeyUsername = username.trim() || undefined;\n \n try {\n credentialId = await authService.createPasskey(passkeyUsername);\n } catch (passkeyError) {\n console.error('[RegistrationFlow] Passkey creation failed:', passkeyError);\n throw new Error('Unable to create passkey. Please try again.');\n }\n\n let keyInfo;\n try {\n keyInfo = await authService.createNostrKey(credentialId);\n } catch (nostrKeyError) {\n console.error('[RegistrationFlow] Failed to create Nostr key from passkey:', nostrKeyError);\n throw new Error('Unable to finalize account. Please try again.');\n }\n\n authService.setCurrentKeyInfo(keyInfo);\n setAuthenticated(keyInfo);\n setStep('success');\n\n if (onSuccess) {\n setTimeout(() => {\n onSuccess();\n }, 1500);\n }\n } catch (err) {\n console.error('[RegistrationFlow] Registration error:', err);\n setError('Registration failed. Please try again.');\n setStep('info');\n setIsLoading(false);\n }\n };\n\n return (\n <div className=\"auth-container\">\n <div className=\"auth-card\">\n <h1>Create Account</h1>\n <p className=\"auth-description\">\n Create a new decentralized Identity. Your identity will be securely derived from a passkey.\n </p>\n\n {step === 'info' && (\n <>\n <div className=\"auth-features\">\n <div className=\"feature-item\">\n <span className=\"feature-icon\">🔐</span>\n <span>Phishing-resistant authentication</span>\n </div>\n <div className=\"feature-item\">\n <span className=\"feature-icon\">📱</span>\n <span>Biometric authentication support</span>\n </div>\n <div className=\"feature-item\">\n <span className=\"feature-icon\">🌐</span>\n <span>Cross-device synchronization</span>\n </div>\n </div>\n\n <div className=\"username-section\">\n <label htmlFor=\"username\" className=\"username-label\">\n Name (Optional)\n </label>\n <input\n id=\"username\"\n type=\"text\"\n className=\"username-input\"\n placeholder=\"Enter a name for this passkey\"\n value={username}\n onChange={(e) => setUsername(e.target.value)}\n disabled={isLoading}\n maxLength={maxUsernameLength}\n />\n </div>\n\n {error && <div className=\"error-message\">{error}</div>}\n\n <button\n className=\"auth-button primary\"\n onClick={handleRegister}\n disabled={isLoading}\n >\n {isLoading ? 'Creating...' : 'Create Account'}\n </button>\n </>\n )}\n\n {step === 'creating' && (\n <div className=\"loading-state\">\n <div className=\"spinner\"></div>\n <p>Creating your passkey...</p>\n <p className=\"loading-hint\">Please follow your browser's authentication prompt</p>\n <p className=\"loading-hint-small\">\n 💡 Using your system's native passkey manager (Touch ID, Face ID, Windows Hello, etc.)\n </p>\n </div>\n )}\n\n {step === 'success' && (\n <div className=\"success-state\">\n <div className=\"success-icon\">✓</div>\n <p>Account created successfully!</p>\n <p className=\"success-hint\">Redirecting to profile setup...</p>\n </div>\n )}\n </div>\n </div>\n );\n}\n\n","import DOMPurify from 'isomorphic-dompurify';\n\nconst sanitizeOptions = {\n ALLOWED_TAGS: [],\n ALLOWED_ATTR: [],\n};\n\nexport const sanitizeText = (value: string): string =>\n DOMPurify.sanitize(value, sanitizeOptions);\n","import { useState } from 'react';\nimport { useZxing } from 'react-zxing';\n\ninterface BarcodeScannerProps {\n /** Called when a code is successfully read */\n onDecode: (value: string) => void;\n /** Optional flag to hide the scanner */\n active?: boolean;\n}\n\nexport const BarcodeScanner = ({ onDecode, active = true }: BarcodeScannerProps) => {\n const [error, setError] = useState<string | null>(null);\n const { ref } = useZxing({\n onDecodeResult: (result) => {\n onDecode(result.getText());\n },\n onError: (e: unknown) => {\n const errorMessage = e instanceof Error ? e.message : 'Camera error';\n setError(errorMessage);\n },\n });\n\n if (!active) return null;\n\n return (\n <div style={{ position: 'relative', width: '100%', maxWidth: '400px' }}>\n <video\n ref={ref as React.LegacyRef<HTMLVideoElement>}\n style={{ width: '100%', borderRadius: '8px' }}\n playsInline\n muted\n />\n {error && (\n <p style={{ color: 'red', marginTop: '0.5rem' }}>\n {error}\n </p>\n )}\n </div>\n );\n};\n\n","import { useState, useEffect } from 'react';\nimport type { AuthService } from '../../services/auth.service';\nimport type { RelayService } from '../../services/relay.service';\nimport type { AuthState } from '../../types/auth';\nimport type { ProfileMetadata, FollowEntry } from '../../types/nostr';\nimport { sanitizeText } from '../../utils/sanitize';\nimport { isValidHttpUrl } from '../../utils/validation';\nimport { BarcodeScanner } from './BarcodeScanner';\nimport './Membership.css';\n\ninterface ProfileWithPubkey extends ProfileMetadata {\n pubkey: string;\n}\n\ninterface MembershipPageProps {\n authService: AuthService;\n relayService: RelayService;\n publicKey: string | null;\n onUnauthenticated?: () => void;\n}\n\nconst TrustInfo = () => (\n <section className=\"trust-info\">\n <h2>Building Trust with Verifiable Relationship Credentials</h2>\n <p>\n Beyond the Personhood Credential, members can create <strong>Verifiable Relationship\n Credentials (VRCs)</strong>. A VRC is issued directly between two members – for example,\n by scanning a QR‑code at a meetup – and certifies a first‑hand trust link.\n </p>\n <p>\n Each VRC becomes a node in a decentralized trust graph. When you add a member, the\n system records a <strong>role</strong> tag that ties the new\n participant to your existing graph, enabling permissionless access to network‑state\n resources while keeping the underlying data private.\n </p>\n <p>\n The graph grows organically: trusted authorities issue PHCs, and members continuously\n enrich the network with peer‑generated VRCs, creating a resilient, scalable web of\n verified participants.\n </p>\n </section>\n);\n\nexport function MembershipPage({\n authService,\n relayService,\n publicKey,\n onUnauthenticated,\n}: MembershipPageProps) {\n const [isLoading, setIsLoading] = useState(false);\n const [isSaving, setIsSaving] = useState(false);\n const [saveMessage, setSaveMessage] = useState<string | null>(null);\n const [searchQuery, setSearchQuery] = useState('');\n const [profiles, setProfiles] = useState<ProfileWithPubkey[]>([]);\n const [members, setMembers] = useState<FollowEntry[]>([]);\n const [memberProfiles, setMemberProfiles] = useState<Map<string, ProfileMetadata>>(new Map());\n const [showScanner, setShowScanner] = useState(false);\n\n useEffect(() => {\n if (!publicKey) {\n if (onUnauthenticated) {\n onUnauthenticated();\n }\n return;\n }\n\n loadFollowList();\n }, [publicKey]);\n\n useEffect(() => {\n if (members.length > 0) {\n loadMemberProfiles();\n }\n }, [members]);\n\n const handleDecoded = (decoded: string) => {\n setSearchQuery(decoded);\n setShowScanner(false);\n handleSearch();\n };\n\n const loadFollowList = async () => {\n if (!publicKey) return;\n\n setIsLoading(true);\n try {\n const followList = await relayService.fetchFollowList(publicKey);\n setMembers(followList);\n } catch (error) {\n console.error('Failed to load follow list:', error);\n setSaveMessage('Failed to load membership list');\n } finally {\n setIsLoading(false);\n }\n };\n\n const loadMemberProfiles = async () => {\n if (members.length === 0) return;\n\n try {\n const pubkeys = members.map((m) => m.pubkey);\n const profilesMap = await relayService.fetchMultipleProfiles(pubkeys);\n setMemberProfiles(profilesMap);\n } catch (error) {\n console.error('Failed to load member profiles:', error);\n }\n };\n\n const handleSearch = async () => {\n if (!searchQuery.trim()) {\n setIsLoading(true);\n try {\n const profilesMap = await relayService.queryProfiles([], 50);\n const profilesList: ProfileWithPubkey[] = [];\n profilesMap.forEach((profile, pubkey) => {\n profilesList.push({ ...profile, pubkey });\n });\n setProfiles(profilesList);\n } catch (error) {\n console.error('Failed to query profiles:', error);\n setSaveMessage('Failed to search profiles');\n } finally {\n setIsLoading(false);\n }\n return;\n }\n\n const trimmedQuery = searchQuery.trim();\n if (trimmedQuery.length !== 64 || !/^[0-9a-fA-F]+$/.test(trimmedQuery)) {\n setSaveMessage('Invalid pubkey format. Must be 64 hex characters.');\n setTimeout(() => setSaveMessage(null), 3000);\n return;\n }\n\n setIsLoading(true);\n try {\n const profilesMap = await relayService.queryProfiles([trimmedQuery], 1);\n const profilesList: ProfileWithPubkey[] = [];\n profilesMap.forEach((profile, pubkey) => {\n profilesList.push({ ...profile, pubkey });\n });\n setProfiles(profilesList);\n if (profilesList.length === 0) {\n setSaveMessage('No profile found for this pubkey');\n setTimeout(() => setSaveMessage(null), 3000);\n }\n } catch (error) {\n console.error('Failed to query profile:', error);\n setSaveMessage('Failed to search profile');\n } finally {\n setIsLoading(false);\n }\n };\n\n const handleAddMember = async (pubkey: string) => {\n if (!publicKey) return;\n\n if (members.some((m) => m.pubkey === pubkey)) {\n setSaveMessage('User is already a member');\n setTimeout(() => setSaveMessage(null), 3000);\n return;\n }\n\n setIsSaving(true);\n try {\n const newMembers: FollowEntry[] = [\n ...members,\n {\n pubkey,\n },\n ];\n\n await relayService.publishFollowList(publicKey, newMembers, (event) =>\n authService.signEvent(event)\n );\n\n setMembers(newMembers);\n setSaveMessage('Member added successfully!');\n setTimeout(() => setSaveMessage(null), 3000);\n } catch (error) {\n console.error('Failed to add member:', error);\n setSaveMessage('Failed to add member. Please try again.');\n } finally {\n setIsSaving(false);\n }\n };\n\n const handleRemoveMember = async (pubkey: string) => {\n if (!publicKey) return;\n\n setIsSaving(true);\n try {\n const newMembers = members.filter((m) => m.pubkey !== pubkey);\n\n await relayService.publishFollowList(publicKey, newMembers, (event) =>\n authService.signEvent(event)\n );\n\n setMembers(newMembers);\n setSaveMessage('Member removed successfully!');\n setTimeout(() => setSaveMessage(null), 3000);\n } catch (error) {\n console.error('Failed to remove member:', error);\n setSaveMessage('Failed to remove member. Please try again.');\n } finally {\n setIsSaving(false);\n }\n };\n\n const getProfileDisplayName = (profile: ProfileMetadata, pubkey: string): string => {\n return profile.display_name || profile.name || pubkey.slice(0, 16) + '...';\n };\n\n const formatPubkey = (pubkey: string): string => {\n return `${pubkey.slice(0, 8)}...${pubkey.slice(-8)}`;\n };\n\n if (isLoading && members.length === 0) {\n return (\n <div className=\"membership-container\">\n <div className=\"loading-state\">\n <div className=\"spinner\"></div>\n <p>Loading membership list...</p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"membership-container\">\n <div className=\"membership-card\">\n <h1>Membership Management</h1>\n <TrustInfo />\n <p className=\"membership-description\">\n Query applicants and manage the membership list.\n </p>\n\n {saveMessage && (\n <div\n className={`save-message ${\n saveMessage.includes('Error') || saveMessage.includes('Failed')\n ? 'error'\n : 'success'\n }`}\n >\n {saveMessage}\n </div>\n )}\n\n <div className=\"search-section\">\n <h2>Add Member</h2>\n <div className=\"search-form\">\n <input\n type=\"text\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n onKeyPress={(e) => e.key === 'Enter' && handleSearch()}\n placeholder=\"Enter pubkey or leave empty for recent profiles\"\n className=\"search-input\"\n disabled={isLoading}\n />\n <button\n onClick={handleSearch}\n className=\"search-button\"\n disabled={isLoading || isSaving}\n >\n {isLoading ? 'Searching...' : 'Search'}\n </button>\n <button\n onClick={() => setShowScanner((prev) => !prev)}\n className=\"scanner-toggle\"\n disabled={isLoading || isSaving}\n >\n {showScanner ? 'Close Scanner' : 'Scan QR'}\n </button>\n </div>\n\n {showScanner && (\n <div style={{ marginTop: '1rem' }}>\n <BarcodeScanner onDecode={handleDecoded} active={true} />\n <p style={{ fontSize: '0.85rem', marginTop: '0.5rem' }}>\n Point at QR‑code containing a pubkey\n </p>\n </div>\n )}\n\n {profiles.length > 0 && (\n <div className=\"profiles-list\">\n <h3>Search Results</h3>\n {profiles.map((profile) => {\n const isMember = members.some((m) => m.pubkey === profile.pubkey);\n return (\n <div key={profile.pubkey} className=\"profile-item\">\n <div className=\"profile-info\">\n {profile.picture && isValidHttpUrl(profile.picture) && (\n <img\n src={profile.picture}\n alt={sanitizeText(getProfileDisplayName(profile, profile.pubkey))}\n className=\"profile-avatar\"\n loading=\"lazy\"\n referrerPolicy=\"no-referrer\"\n onError={(event) => {\n event.currentTarget.style.display = 'none';\n }}\n />\n )}\n <div className=\"profile-details\">\n <div className=\"profile-name\">\n {sanitizeText(getProfileDisplayName(profile, profile.pubkey))}\n </div>\n <div className=\"profile-pubkey\">{formatPubkey(profile.pubkey)}</div>\n {profile.about && (\n <div className=\"profile-about\">{sanitizeText(profile.about)}</div>\n )}\n </div>\n </div>\n <button\n onClick={() =>\n isMember\n ? handleRemoveMember(profile.pubkey)\n : handleAddMember(profile.pubkey)\n }\n className={`member-button ${isMember ? 'remove' : 'add'}`}\n disabled={isSaving}\n >\n {isMember ? 'Remove' : 'Add Member'}\n </button>\n </div>\n );\n })}\n </div>\n )}\n </div>\n\n <div className=\"members-section\">\n <h2>Current Members ({members.length})</h2>\n {members.length === 0 ? (\n <p className=\"empty-message\">No members yet. Add members from search results above.</p>\n ) : (\n <div className=\"members-list\">\n {members.map((member) => {\n const profile = memberProfiles.get(member.pubkey);\n return (\n <div key={member.pubkey} className=\"member-item\">\n <div className=\"profile-info\">\n {profile?.picture && isValidHttpUrl(profile.picture) && (\n <img\n src={profile.picture}\n alt={sanitizeText(getProfileDisplayName(profile || {}, member.pubkey))}\n className=\"profile-avatar\"\n loading=\"lazy\"\n referrerPolicy=\"no-referrer\"\n onError={(event) => {\n event.currentTarget.style.display = 'none';\n }}\n />\n )}\n <div className=\"profile-details\">\n <div className=\"profile-name\">\n {profile\n ? sanitizeText(getProfileDisplayName(profile, member.pubkey))\n : formatPubkey(member.pubkey)}\n </div>\n <div className=\"profile-pubkey\">{formatPubkey(member.pubkey)}</div>\n {profile?.about && (\n <div className=\"profile-about\">{sanitizeText(profile.about)}</div>\n )}\n {member.petname && (\n <div className=\"profile-petname\">Name: {sanitizeText(member.petname)}</div>\n )}\n </div>\n </div>\n <button\n onClick={() => handleRemoveMember(member.pubkey)}\n className=\"member-button remove\"\n disabled={isSaving}\n >\n Remove\n </button>\n </div>\n );\n })}\n </div>\n )}\n </div>\n </div>\n </div>\n );\n}\n\n","import { useState, useEffect } from 'react';\nimport type { AuthService } from '../../services/auth.service';\nimport type { RelayService } from '../../services/relay.service';\nimport type { AuthState } from '../../types/auth';\nimport type { ProfileMetadata } from '../../types/nostr';\nimport { sanitizeText } from '../../utils/sanitize';\nimport { isValidPubkey, isValidRoleTag } from '../../utils/validation';\nimport './Profile.css';\n\ninterface ProfilePageProps {\n authService: AuthService;\n relayService: RelayService;\n publicKey: string | null;\n onUnauthenticated?: () => void;\n onSuccess?: () => void;\n onRoleSuggestion?: (about: string) => Promise<string | null>;\n}\n\nconst PersonhoodInfo = () => (\n <section className=\"personhood-info\">\n <p>\n Network‑state members receive a <strong>Personhood Credential (PHC)</strong> from a trusted authority.\n The PHC attests that the holder is a unique, real individual. Because the credential lives\n in your passport, you can present a <em>zero‑knowledge proof</em> that you are verified.\n </p>\n <p>\n In this form we compare the name you enter with the name disclosed by your verified\n passport proof. If the two match, the profile will be saved as your business card credential together with a\n passport tag that references the PHC's unique identifier.\n </p>\n </section>\n);\n\nconst MAX_NAME_LENGTH = 100;\nconst MAX_ABOUT_LENGTH = 1000;\nconst MAX_URL_LENGTH = 2048;\n\nexport function ProfilePage({\n authService,\n relayService,\n publicKey,\n onUnauthenticated,\n onSuccess,\n onRoleSuggestion,\n}: ProfilePageProps) {\n const [isLoading, setIsLoading] = useState(false);\n const [isSaving, setIsSaving] = useState(false);\n const [saveMessage, setSaveMessage] = useState<string | null>(null);\n const [suggestedRole, setSuggestedRole] = useState<string | null>(null);\n const [isGettingRole, setIsGettingRole] = useState(false);\n const [formData, setFormData] = useState<ProfileMetadata>({\n name: '',\n display_name: '',\n about: '',\n picture: '',\n website: '',\n });\n\n useEffect(() => {\n if (!publicKey) {\n if (onUnauthenticated) {\n onUnauthenticated();\n }\n return;\n }\n\n loadProfile();\n }, [publicKey]);\n\n const loadProfile = async () => {\n if (!publicKey) return;\n\n setIsLoading(true);\n try {\n const [profile, roleTag] = await Promise.all([\n relayService.fetchProfile(publicKey),\n relayService.fetchProfileRoleTag(publicKey),\n ]);\n \n if (profile) {\n setFormData({\n name: sanitizeText(profile.name || ''),\n display_name: sanitizeText(profile.display_name || ''),\n about: sanitizeText(profile.about || ''),\n picture: profile.picture || '',\n website: profile.website || '',\n });\n }\n \n if (roleTag) {\n setSuggestedRole(roleTag);\n }\n } catch (error) {\n console.error('Failed to load profile:', error);\n } finally {\n setIsLoading(false);\n }\n };\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n if (!publicKey) return;\n\n setIsSaving(true);\n setSaveMessage(null);\n\n try {\n const tags: string[][] = [];\n if (!isValidPubkey(publicKey)) {\n throw new Error('Invalid public key format');\n }\n\n // Suggest role based on \"about\" field if it has content and callback provided\n if (formData.about && formData.about.trim().length > 0 && onRoleSuggestion) {\n setIsGettingRole(true);\n try {\n const roleSuggestion = await onRoleSuggestion(formData.about);\n const candidate = roleSuggestion ? roleSuggestion.trim() : '';\n if (candidate && isValidRoleTag(candidate)) {\n tags.push(['role', candidate]);\n setSuggestedRole(candidate);\n } else {\n setSuggestedRole(null);\n }\n } catch (error) {\n console.error('Failed to get role suggestion:', error);\n setSuggestedRole(null);\n } finally {\n setIsGettingRole(false);\n }\n } else {\n setSuggestedRole(null);\n }\n\n const profileEvent = {\n kind: 0,\n content: JSON.stringify({\n ...formData,\n name: sanitizeText(formData.name || ''),\n display_name: sanitizeText(formData.display_name || ''),\n about: sanitizeText(formData.about || ''),\n }),\n created_at: Math.floor(Date.now() / 1000),\n tags,\n };\n\n const follows = {\n kind: 3,\n content: \"\",\n created_at: Math.floor(Date.now() / 1000),\n tags: [\n (() => {\n const followTag: string[] = ['p', publicKey];\n const relayUrl = relayService.getRelays()[0];\n if (relayUrl) {\n followTag.push(relayUrl);\n }\n if (formData.name) {\n followTag.push(formData.name);\n }\n return followTag;\n })(),\n ],\n };\n\n const signedProfile = await authService.signEvent(profileEvent);\n const signedFollows = await authService.signEvent(follows);\n\n await relayService.publishEvent(signedProfile);\n await relayService.publishEvent(signedFollows);\n\n setSaveMessage('Profile saved successfully!');\n setTimeout(() => {\n setSaveMessage(null);\n }, 3000);\n\n if (onSuccess) {\n onSuccess();\n }\n } catch (error) {\n console.error('Failed to save profile:', error);\n setSaveMessage('Failed to save profile. Please try again.');\n } finally {\n setIsSaving(false);\n }\n };\n\n const handleChange = (\n e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>\n ) => {\n const { name, value } = e.target;\n setFormData((prev) => ({\n ...prev,\n [name]: value,\n }));\n };\n\n if (isLoading) {\n return (\n <div className=\"profile-container\">\n <div className=\"loading-state\">\n <div className=\"spinner\"></div>\n <p>Loading profile...</p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"profile-container\">\n <div className=\"profile-card\">\n <h1>Profile Setup</h1>\n <PersonhoodInfo />\n\n <form onSubmit={handleSubmit} className=\"profile-form\">\n <div className=\"form-group\">\n <label htmlFor=\"name\">First Name</label>\n <input\n type=\"text\"\n id=\"name\"\n name=\"name\"\n value={formData.name || ''}\n onChange={handleChange}\n placeholder=\"Your username\"\n maxLength={MAX_NAME_LENGTH}\n />\n </div>\n\n <div className=\"form-group\">\n <label htmlFor=\"display_name\">Last Name</label>\n <input\n type=\"text\"\n id=\"display_name\"\n name=\"display_name\"\n value={formData.display_name || ''}\n onChange={handleChange}\n placeholder=\"Your Last Name\"\n maxLength={MAX_NAME_LENGTH}\n />\n </div>\n\n <div className=\"form-group\">\n <label htmlFor=\"about\">About</label>\n <textarea\n id=\"about\"\n name=\"about\"\n value={formData.about || ''}\n onChange={handleChange}\n placeholder=\"Tell us about yourself\"\n rows={4}\n maxLength={MAX_ABOUT_LENGTH}\n />\n </div>\n\n <div className=\"form-group\">\n <label htmlFor=\"picture\">Profile Picture URL</label>\n <input\n type=\"url\"\n id=\"picture\"\n name=\"picture\"\n value={formData.picture || ''}\n onChange={handleChange}\n placeholder=\"https://example.com/avatar.jpg\"\n maxLength={MAX_URL_LENGTH}\n />\n </div>\n\n <div className=\"form-group\">\n <label htmlFor=\"website\">LinkedIn</label>\n <input\n type=\"url\"\n id=\"website\"\n name=\"website\"\n value={formData.website || ''}\n onChange={handleChange}\n placeholder=\"https://linkedin.com/example\"\n maxLength={MAX_URL_LENGTH}\n />\n </div>\n\n {saveMessage && (\n <div className={`save-message ${saveMessage.includes('Error') ? 'error' : 'success'}`}>\n {saveMessage}\n </div>\n )}\n\n {(isGettingRole || suggestedRole) && (\n <div className=\"role-tag-container\">\n {isGettingRole ? (\n <>\n <div className=\"role-tag-label\">Getting AI suggestion...</div>\n <div className=\"role-tag-loading\">\n <div className=\"role-tag-spinner\"></div>\n </div>\n </>\n ) : suggestedRole ? (\n <>\n <div className=\"role-tag-label\">AI Suggested Role:</div>\n <div className=\"role-tag\">\n {sanitizeText(suggestedRole)}\n </div>\n </>\n ) : null}\n </div>\n )}\n\n <div className=\"form-actions\">\n <button type=\"submit\" className=\"save-button\" disabled={isSaving}>\n {isSaving ? 'Saving...' : 'Save Profile'}\n </button>\n </div>\n </form>\n </div>\n </div>\n );\n}\n\n","import { useEffect } from 'react';\nimport type { AuthService } from '../services/auth.service';\nimport type { AuthState } from '../types/auth';\n\n/**\n * Hook to initialize auth state on app load\n */\nexport function useAuthInit(\n authService: AuthService,\n setAuthenticated: AuthState['setAuthenticated']\n): void {\n useEffect(() => {\n if (authService.hasKeyInfo()) {\n const keyInfo = authService.getCurrentKeyInfo();\n if (keyInfo) {\n setAuthenticated(keyInfo);\n }\n }\n }, [authService, setAuthenticated]);\n}\n\n","import { create } from 'zustand';\nimport type { NostrKeyInfo } from '../utils';\nimport type { AuthState } from '../types/auth';\n\nexport const createAuthStore = () => {\n return create<AuthState>((set) => ({\n isAuthenticated: false,\n publicKey: null,\n keyInfo: null,\n loginError: null,\n setAuthenticated: (keyInfo: NostrKeyInfo | null) => {\n set({\n isAuthenticated: !!keyInfo,\n publicKey: keyInfo?.pubkey || null,\n keyInfo,\n loginError: null,\n });\n },\n setLoginError: (error: string | null) => {\n set({ loginError: error });\n },\n logout: () => {\n set({\n isAuthenticated: false,\n publicKey: null,\n keyInfo: null,\n loginError: null,\n });\n },\n }));\n};\n\n// Default singleton instance\nexport const useAuthStore = createAuthStore();\n\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/services/auth.service.ts","../src/utils/validation.ts","../src/services/relay.service.ts","../src/components/auth/LoginButton.tsx","../src/components/auth/RegistrationFlow.tsx","../src/utils/sanitize.ts","../src/components/membership/BarcodeScanner.tsx","../src/components/membership/MembershipPage.tsx","../src/components/profile/ProfilePage.tsx","../src/hooks/useAuth.ts","../src/store/authStore.ts"],"sourcesContent":["import { NosskeyManager, type NostrKeyInfo, type NostrEvent, type PasskeyCreationOptions } from '../utils';\nimport type { AuthServiceConfig } from '../types/auth';\n\n/**\n * Service wrapper around NosskeyManager\n * Handles WebAuthn/Passkey integration with Nostr\n */\nexport class AuthService {\n private manager: NosskeyManager | null = null;\n private config: AuthServiceConfig;\n\n constructor(config: AuthServiceConfig = {}) {\n this.config = {\n rpId: config.rpId || (typeof window !== 'undefined' ? window.location.hostname.replace(/^www\\./, '') : 'localhost'),\n rpName: config.rpName || this.getDefaultRpName(),\n storageKey: config.storageKey || 'nsauth_keyinfo',\n cacheTimeoutMs: config.cacheTimeoutMs || 30 * 60 * 1000,\n cacheOnCreation: config.cacheOnCreation !== undefined ? config.cacheOnCreation : true,\n };\n }\n\n private getDefaultRpName(): string {\n if (typeof window === 'undefined') return 'localhost';\n const hostname = window.location.hostname;\n if (hostname.includes('nosskey.app')) return 'nosskey.app';\n return hostname.replace(/^www\\./, '');\n }\n\n /**\n * Initialize the NosskeyManager instance\n */\n private getManager(): NosskeyManager {\n if (!this.manager) {\n this.manager = new NosskeyManager({\n cacheOptions: {\n enabled: true,\n timeoutMs: this.config.cacheTimeoutMs,\n cacheOnCreation: this.config.cacheOnCreation,\n },\n storageOptions: {\n enabled: true,\n storageKey: this.config.storageKey,\n },\n });\n }\n return this.manager;\n }\n\n /**\n * Create a new passkey\n * Uses platform authenticator only (Touch ID, Face ID, Windows Hello)\n */\n async createPasskey(username?: string): Promise<Uint8Array> {\n const manager = this.getManager();\n \n const rpId = this.config.rpId === 'localhost' ? 'localhost' : this.config.rpId;\n const rpName = this.config.rpName;\n \n const trimmedUsername = username?.trim();\n const uniqueUsername = trimmedUsername \n ? trimmedUsername \n : `user-${Date.now()}@example.com`;\n \n const options: PasskeyCreationOptions = {\n rp: {\n id: rpId,\n name: rpName,\n },\n user: {\n name: uniqueUsername,\n displayName: trimmedUsername || 'User',\n },\n authenticatorSelection: {\n authenticatorAttachment: 'platform',\n residentKey: 'preferred',\n userVerification: 'preferred',\n },\n extensions: {\n prf: {},\n },\n };\n \n return await manager.createPasskey(options);\n }\n\n /**\n * Create a new Nostr key from a credential ID\n */\n async createNostrKey(credentialId?: Uint8Array): Promise<NostrKeyInfo> {\n const manager = this.getManager();\n return await manager.createNostrKey(credentialId);\n }\n\n /**\n * Get the current public key\n */\n async getPublicKey(): Promise<string> {\n const manager = this.getManager();\n return await manager.getPublicKey();\n }\n\n /**\n * Sign a Nostr event\n */\n async signEvent(event: NostrEvent): Promise<NostrEvent> {\n const manager = this.getManager();\n return await manager.signEvent(event);\n }\n\n /**\n * Get current key info\n */\n getCurrentKeyInfo(): NostrKeyInfo | null {\n const manager = this.getManager();\n return manager.getCurrentKeyInfo();\n }\n\n /**\n * Set current key info\n */\n setCurrentKeyInfo(keyInfo: NostrKeyInfo): void {\n const manager = this.getManager();\n manager.setCurrentKeyInfo(keyInfo);\n }\n\n /**\n * Check if key info exists\n */\n hasKeyInfo(): boolean {\n const manager = this.getManager();\n return manager.hasKeyInfo();\n }\n\n /**\n * Clear stored key info\n */\n clearStoredKeyInfo(): void {\n const manager = this.getManager();\n manager.clearStoredKeyInfo();\n }\n\n /**\n * Check if PRF is supported\n */\n async isPrfSupported(): Promise<boolean> {\n const { isPrfSupported } = await import('../utils');\n return await isPrfSupported();\n }\n}\n\n","export const MAX_ROLE_LENGTH = 100;\nconst ROLE_PATTERN = /^[a-zA-Z0-9\\s\\-_]+$/;\n\nexport const isValidRoleTag = (role: string): boolean => {\n const trimmed = role.trim();\n return (\n trimmed.length > 0 &&\n trimmed.length <= MAX_ROLE_LENGTH &&\n ROLE_PATTERN.test(trimmed)\n );\n};\n\nexport const isValidPubkey = (pubkey: string): boolean =>\n pubkey.length === 64 && /^[0-9a-fA-F]+$/.test(pubkey);\n\nexport const isValidHttpUrl = (url: string): boolean => {\n try {\n const parsed = new URL(url);\n return ['http:', 'https:'].includes(parsed.protocol);\n } catch {\n return false;\n }\n};\n\nexport const isValidRelayUrl = (url: string): boolean => {\n try {\n const parsed = new URL(url);\n return ['ws:', 'wss:'].includes(parsed.protocol) && parsed.hostname.length > 0;\n } catch {\n return false;\n }\n};\n","import type { EventStore } from 'applesauce-core';\nimport type { NostrEvent } from '../types/nostr';\nimport type { ProfileMetadata, FollowEntry } from '../types/nostr';\nimport type { RelayServiceConfig } from '../types/auth';\nimport { isValidPubkey, isValidRelayUrl, isValidRoleTag } from '../utils/validation';\n\n/**\n * Service for communicating with Nostr relays using applesauce-core\n */\nexport class RelayService {\n private eventStore: EventStore | null = null;\n private relayUrls: string[];\n private defaultRelays = ['wss://relay.damus.io'];\n private readonly maxProfileContentSize = 10000;\n private readonly minProfileQueryIntervalMs = 300;\n private readonly minPublishIntervalMs = 750;\n private readonly lastActionAt = new Map<string, number>();\n\n constructor(config: RelayServiceConfig = {}) {\n this.relayUrls = this.validateRelayUrls(config.relayUrls ?? this.defaultRelays);\n }\n\n /**\n * Initialize with applesauce EventStore\n */\n initialize(eventStore: EventStore): void {\n this.eventStore = eventStore;\n // Set default relays if EventStore has a method for it\n if (eventStore && 'setRelays' in eventStore && typeof eventStore.setRelays === 'function') {\n (eventStore as any).setRelays(this.relayUrls);\n }\n }\n\n /**\n * Set relay URLs\n */\n setRelays(urls: string[]): void {\n this.relayUrls = this.validateRelayUrls(urls);\n if (this.eventStore && 'setRelays' in this.eventStore && typeof this.eventStore.setRelays === 'function') {\n (this.eventStore as any).setRelays(this.relayUrls);\n }\n }\n\n /**\n * Get current relay URLs\n */\n getRelays(): string[] {\n return [...this.relayUrls];\n }\n\n /**\n * Publish an event to relays\n */\n async publishEvent(event: NostrEvent, timeoutMs = 1000): Promise<boolean> {\n if (!this.eventStore) {\n throw new Error('RelayService not initialized. Call initialize() with an EventStore instance.');\n }\n\n await this.enforceRateLimit('publish', this.minPublishIntervalMs);\n return new Promise((resolve, reject) => {\n if (this.relayUrls.length === 0) {\n reject(new Error('No relays configured'));\n return;\n }\n\n // Use EventStore's publish method\n // Note: This is a simplified implementation - actual applesauce API may differ\n const eventStore = this.eventStore as any;\n if (!eventStore || typeof eventStore.publish !== 'function') {\n reject(new Error('EventStore does not support publish method'));\n return;\n }\n const subscription = eventStore.publish(event).subscribe({\n next: (response: any) => {\n if (response?.type === 'OK') {\n subscription.unsubscribe();\n resolve(true);\n }\n },\n error: (error: Error) => {\n subscription.unsubscribe();\n reject(error);\n },\n });\n\n // Timeout fallback\n setTimeout(() => {\n subscription.unsubscribe();\n resolve(false);\n }, timeoutMs);\n });\n }\n\n /**\n * Fetch a profile (Kind 0 event)\n */\n async fetchProfile(pubkey: string): Promise<ProfileMetadata | null> {\n if (!this.eventStore) {\n throw new Error('RelayService not initialized. Call initialize() with an EventStore instance.');\n }\n\n await this.enforceRateLimit('fetch-profile', this.minProfileQueryIntervalMs);\n return new Promise((resolve) => {\n const filter = {\n kinds: [0],\n authors: [pubkey],\n limit: 1,\n };\n\n const eventStore = this.eventStore as any;\n if (!eventStore || typeof eventStore.query !== 'function') {\n resolve(null);\n return;\n }\n const subscription = eventStore.query(filter).subscribe({\n next: (packet: any) => {\n if (packet?.event && packet.event.kind === 0) {\n const metadata = this.parseProfileMetadata(packet.event.content);\n if (metadata) {\n subscription.unsubscribe();\n resolve(metadata);\n return;\n }\n console.error('Failed to parse profile metadata');\n }\n },\n complete: () => {\n subscription.unsubscribe();\n resolve(null);\n },\n error: (error: Error) => {\n console.error('Error fetching profile:', error);\n subscription.unsubscribe();\n resolve(null);\n },\n });\n\n // Timeout\n setTimeout(() => {\n subscription.unsubscribe();\n resolve(null);\n }, 5000);\n });\n }\n\n /**\n * Fetch role tag from profile event (Kind 0)\n */\n async fetchProfileRoleTag(pubkey: string): Promise<string | null> {\n if (!this.eventStore) {\n throw new Error('RelayService not initialized. Call initialize() with an EventStore instance.');\n }\n\n await this.enforceRateLimit('fetch-role-tag', this.minProfileQueryIntervalMs);\n return new Promise((resolve) => {\n const filter = {\n kinds: [0],\n authors: [pubkey],\n limit: 1,\n };\n\n const eventStore = this.eventStore as any;\n if (!eventStore || typeof eventStore.query !== 'function') {\n resolve(null);\n return;\n }\n const subscription = eventStore.query(filter).subscribe({\n next: (packet: any) => {\n if (packet?.event && packet.event.kind === 0) {\n const tags = packet.event.tags || [];\n for (const tag of tags) {\n if (tag[0] === 'role' && tag[1]) {\n const candidate = tag[1].trim();\n if (isValidRoleTag(candidate)) {\n subscription.unsubscribe();\n resolve(candidate);\n return;\n }\n }\n }\n subscription.unsubscribe();\n resolve(null);\n }\n },\n complete: () => {\n subscription.unsubscribe();\n resolve(null);\n },\n error: (error: Error) => {\n console.error('Error fetching profile role tag:', error);\n subscription.unsubscribe();\n resolve(null);\n },\n });\n\n setTimeout(() => {\n subscription.unsubscribe();\n resolve(null);\n }, 5000);\n });\n }\n\n /**\n * Fetch a follow list (Kind 3 event)\n */\n async fetchFollowList(pubkey: string): Promise<FollowEntry[]> {\n if (!this.eventStore) {\n throw new Error('RelayService not initialized. Call initialize() with an EventStore instance.');\n }\n\n await this.enforceRateLimit('fetch-follow-list', this.minProfileQueryIntervalMs);\n return new Promise((resolve) => {\n const filter = {\n kinds: [3],\n authors: [pubkey],\n limit: 1,\n };\n\n const eventStore = this.eventStore as any;\n if (!eventStore || typeof eventStore.query !== 'function') {\n resolve([]);\n return;\n }\n const subscription = eventStore.query(filter).subscribe({\n next: (packet: any) => {\n if (packet?.event && packet.event.kind === 3) {\n const followList: FollowEntry[] = [];\n const tags = packet.event.tags || [];\n\n for (const tag of tags) {\n if (tag[0] === 'p' && tag[1]) {\n followList.push({\n pubkey: tag[1],\n relay: tag[2] || undefined,\n petname: tag[3] || undefined,\n });\n }\n }\n\n subscription.unsubscribe();\n resolve(followList);\n }\n },\n complete: () => {\n subscription.unsubscribe();\n resolve([]);\n },\n error: (error: Error) => {\n console.error('Error fetching follow list:', error);\n subscription.unsubscribe();\n resolve([]);\n },\n });\n\n setTimeout(() => {\n subscription.unsubscribe();\n resolve([]);\n }, 10000);\n });\n }\n\n /**\n * Fetch multiple profiles in batch\n */\n async fetchMultipleProfiles(pubkeys: string[]): Promise<Map<string, ProfileMetadata>> {\n if (!this.eventStore) {\n throw new Error('RelayService not initialized. Call initialize() with an EventStore instance.');\n }\n\n if (pubkeys.length === 0) {\n return new Map();\n }\n\n await this.enforceRateLimit('fetch-multiple-profiles', this.minProfileQueryIntervalMs);\n return new Promise((resolve) => {\n const profiles = new Map<string, ProfileMetadata>();\n const filter = {\n kinds: [0],\n authors: pubkeys,\n };\n\n const eventStore = this.eventStore as any;\n if (!eventStore || typeof eventStore.query !== 'function') {\n resolve(new Map());\n return;\n }\n const subscription = eventStore.query(filter).subscribe({\n next: (packet: any) => {\n if (packet?.event && packet.event.kind === 0 && packet.event.pubkey) {\n const metadata = this.parseProfileMetadata(packet.event.content);\n if (metadata) {\n profiles.set(packet.event.pubkey, metadata);\n } else {\n console.error('Failed to parse profile metadata');\n }\n }\n },\n complete: () => {\n subscription.unsubscribe();\n resolve(profiles);\n },\n error: (error: Error) => {\n console.error('Error fetching profiles:', error);\n subscription.unsubscribe();\n resolve(profiles);\n },\n });\n\n setTimeout(() => {\n subscription.unsubscribe();\n resolve(profiles);\n }, 1000);\n });\n }\n\n /**\n * Query kind 0 events (profiles) by pubkey\n * If pubkeys array is empty, fetches recent kind 0 events\n */\n async queryProfiles(pubkeys: string[] = [], limit = 100): Promise<Map<string, ProfileMetadata>> {\n if (!this.eventStore) {\n throw new Error('RelayService not initialized. Call initialize() with an EventStore instance.');\n }\n\n await this.enforceRateLimit('query-profiles', this.minProfileQueryIntervalMs);\n return new Promise((resolve) => {\n const profiles = new Map<string, { metadata: ProfileMetadata; timestamp: number }>();\n const filter: any = {\n kinds: [0],\n limit,\n };\n\n if (pubkeys.length > 0) {\n filter.authors = pubkeys;\n }\n\n const eventStore = this.eventStore as any;\n if (!eventStore || typeof eventStore.query !== 'function') {\n resolve(new Map());\n return;\n }\n const subscription = eventStore.query(filter).subscribe({\n next: (packet: any) => {\n if (packet?.event && packet.event.kind === 0 && packet.event.pubkey) {\n const metadata = this.parseProfileMetadata(packet.event.content);\n if (metadata) {\n const timestamp = packet.event.created_at || 0;\n const existing = profiles.get(packet.event.pubkey);\n if (!existing || timestamp > existing.timestamp) {\n profiles.set(packet.event.pubkey, { metadata, timestamp });\n }\n } else {\n console.error('Failed to parse profile metadata');\n }\n }\n },\n complete: () => {\n subscription.unsubscribe();\n const result = new Map<string, ProfileMetadata>();\n profiles.forEach((value, pubkey) => {\n result.set(pubkey, value.metadata);\n });\n resolve(result);\n },\n error: (error: Error) => {\n console.error('Error querying profiles:', error);\n subscription.unsubscribe();\n const result = new Map<string, ProfileMetadata>();\n profiles.forEach((value, pubkey) => {\n result.set(pubkey, value.metadata);\n });\n resolve(result);\n },\n });\n\n setTimeout(() => {\n subscription.unsubscribe();\n const result = new Map<string, ProfileMetadata>();\n profiles.forEach((value, pubkey) => {\n result.set(pubkey, value.metadata);\n });\n resolve(result);\n }, 10000);\n });\n }\n\n /**\n * Publish or update a kind 3 event (follow list/contacts)\n */\n async publishFollowList(\n pubkey: string,\n followList: FollowEntry[],\n signEvent: (event: NostrEvent) => Promise<NostrEvent>\n ): Promise<boolean> {\n const tags: string[][] = followList.map((entry) => {\n if (!isValidPubkey(entry.pubkey)) {\n throw new Error('Invalid pubkey format for follow list entry.');\n }\n if (entry.relay && !isValidRelayUrl(entry.relay)) {\n throw new Error('Invalid relay URL format for follow list entry.');\n }\n const tag: string[] = ['p', entry.pubkey];\n if (entry.relay) {\n tag.push(entry.relay);\n }\n if (entry.petname) {\n tag.push(entry.petname);\n }\n return tag;\n });\n\n const event: NostrEvent = {\n kind: 3,\n content: '',\n created_at: Math.floor(Date.now() / 1000),\n tags,\n };\n\n const signedEvent = await signEvent(event);\n return await this.publishEvent(signedEvent);\n }\n\n private validateRelayUrls(urls: string[]): string[] {\n if (urls.length === 0) {\n return [];\n }\n const validUrls = urls.filter((url) => isValidRelayUrl(url));\n if (validUrls.length !== urls.length) {\n throw new Error('Invalid relay URL format');\n }\n return validUrls;\n }\n\n private parseProfileMetadata(content: string): ProfileMetadata | null {\n if (content.length > this.maxProfileContentSize) {\n return null;\n }\n try {\n const parsed = JSON.parse(content);\n if (!parsed || typeof parsed !== 'object') {\n return null;\n }\n const metadata: ProfileMetadata = {};\n if (typeof (parsed as ProfileMetadata).name === 'string') {\n metadata.name = (parsed as ProfileMetadata).name;\n }\n if (typeof (parsed as ProfileMetadata).display_name === 'string') {\n metadata.display_name = (parsed as ProfileMetadata).display_name;\n }\n if (typeof (parsed as ProfileMetadata).about === 'string') {\n metadata.about = (parsed as ProfileMetadata).about;\n }\n if (typeof (parsed as ProfileMetadata).picture === 'string') {\n metadata.picture = (parsed as ProfileMetadata).picture;\n }\n if (typeof (parsed as ProfileMetadata).website === 'string') {\n metadata.website = (parsed as ProfileMetadata).website;\n }\n return metadata;\n } catch (error) {\n console.error('Failed to parse profile metadata:', error);\n return null;\n }\n }\n\n private async enforceRateLimit(action: string, minIntervalMs: number): Promise<void> {\n const lastAt = this.lastActionAt.get(action) ?? 0;\n const now = Date.now();\n const waitMs = minIntervalMs - (now - lastAt);\n if (waitMs > 0) {\n await new Promise((resolve) => setTimeout(resolve, waitMs));\n }\n this.lastActionAt.set(action, Date.now());\n }\n}\n\n","import { useState } from 'react';\nimport type { AuthService } from '../../services/auth.service';\nimport type { AuthState } from '../../types/auth';\nimport './Auth.css';\n\ninterface LoginButtonProps {\n authService: AuthService;\n setAuthenticated: AuthState['setAuthenticated'];\n setLoginError: AuthState['setLoginError'];\n onSuccess?: () => void;\n}\n\nexport function LoginButton({\n authService,\n setAuthenticated,\n setLoginError,\n onSuccess,\n}: LoginButtonProps) {\n const [isLoading, setIsLoading] = useState(false);\n\n const handleLogin = async () => {\n setIsLoading(true);\n setLoginError(null);\n\n try {\n if (!authService.hasKeyInfo()) {\n throw new Error('No account found. Please register first.');\n }\n\n const keyInfo = authService.getCurrentKeyInfo();\n if (!keyInfo) {\n throw new Error('Failed to load account information.');\n }\n\n await authService.getPublicKey();\n setAuthenticated(keyInfo);\n\n if (onSuccess) {\n onSuccess();\n }\n } catch (err) {\n console.error('Login error:', err);\n const errorMessage = err instanceof Error ? err.message : 'Failed to login';\n setLoginError(errorMessage);\n setIsLoading(false);\n }\n };\n\n return (\n <div className=\"login-button-container\">\n <button\n className=\"auth-button secondary\"\n onClick={handleLogin}\n disabled={isLoading}\n >\n {isLoading ? 'Logging in...' : 'Login'}\n </button>\n </div>\n );\n}\n\n","import { useState } from 'react';\nimport type { AuthService } from '../../services/auth.service';\nimport type { AuthState } from '../../types/auth';\nimport './Auth.css';\n\ninterface RegistrationFlowProps {\n authService: AuthService;\n setAuthenticated: AuthState['setAuthenticated'];\n onSuccess?: () => void;\n}\n\nexport function RegistrationFlow({\n authService,\n setAuthenticated,\n onSuccess,\n}: RegistrationFlowProps) {\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [step, setStep] = useState<'info' | 'creating' | 'success'>('info');\n const [username, setUsername] = useState('');\n const maxUsernameLength = 100;\n\n const handleRegister = async () => {\n setIsLoading(true);\n setError(null);\n setStep('creating');\n\n try {\n authService.clearStoredKeyInfo();\n\n let credentialId: Uint8Array;\n const passkeyUsername = username.trim() || undefined;\n \n try {\n credentialId = await authService.createPasskey(passkeyUsername);\n } catch (passkeyError) {\n console.error('[RegistrationFlow] Passkey creation failed:', passkeyError);\n throw new Error('Unable to create passkey. Please try again.');\n }\n\n let keyInfo;\n try {\n keyInfo = await authService.createNostrKey(credentialId);\n } catch (nostrKeyError) {\n console.error('[RegistrationFlow] Failed to create Nostr key from passkey:', nostrKeyError);\n throw new Error('Unable to finalize account. Please try again.');\n }\n\n authService.setCurrentKeyInfo(keyInfo);\n setAuthenticated(keyInfo);\n setStep('success');\n\n if (onSuccess) {\n setTimeout(() => {\n onSuccess();\n }, 1500);\n }\n } catch (err) {\n console.error('[RegistrationFlow] Registration error:', err);\n setError('Registration failed. Please try again.');\n setStep('info');\n setIsLoading(false);\n }\n };\n\n return (\n <div className=\"auth-container\">\n <div className=\"auth-card\">\n <h1>Create Account</h1>\n <p className=\"auth-description\">\n Create a new decentralized Identity. Your identity will be securely derived from a passkey.\n </p>\n\n {step === 'info' && (\n <>\n <div className=\"auth-features\">\n <div className=\"feature-item\">\n <span className=\"feature-icon\">🔐</span>\n <span>Phishing-resistant authentication</span>\n </div>\n <div className=\"feature-item\">\n <span className=\"feature-icon\">📱</span>\n <span>Biometric authentication support</span>\n </div>\n <div className=\"feature-item\">\n <span className=\"feature-icon\">🌐</span>\n <span>Cross-device synchronization</span>\n </div>\n </div>\n\n <div className=\"username-section\">\n <label htmlFor=\"username\" className=\"username-label\">\n Name (Optional)\n </label>\n <input\n id=\"username\"\n type=\"text\"\n className=\"username-input\"\n placeholder=\"Enter a name for this passkey\"\n value={username}\n onChange={(e) => setUsername(e.target.value)}\n disabled={isLoading}\n maxLength={maxUsernameLength}\n />\n </div>\n\n {error && <div className=\"error-message\">{error}</div>}\n\n <button\n className=\"auth-button primary\"\n onClick={handleRegister}\n disabled={isLoading}\n >\n {isLoading ? 'Creating...' : 'Create Account'}\n </button>\n </>\n )}\n\n {step === 'creating' && (\n <div className=\"loading-state\">\n <div className=\"spinner\"></div>\n <p>Creating your passkey...</p>\n <p className=\"loading-hint\">Please follow your browser's authentication prompt</p>\n <p className=\"loading-hint-small\">\n 💡 Using your system's native passkey manager (Touch ID, Face ID, Windows Hello, etc.)\n </p>\n </div>\n )}\n\n {step === 'success' && (\n <div className=\"success-state\">\n <div className=\"success-icon\">✓</div>\n <p>Account created successfully!</p>\n <p className=\"success-hint\">Redirecting to profile setup...</p>\n </div>\n )}\n </div>\n </div>\n );\n}\n\n","import DOMPurify from 'isomorphic-dompurify';\n\nconst sanitizeOptions = {\n ALLOWED_TAGS: [],\n ALLOWED_ATTR: [],\n};\n\nexport const sanitizeText = (value: string): string =>\n DOMPurify.sanitize(value, sanitizeOptions);\n","import { useState } from 'react';\nimport { useZxing } from 'react-zxing';\n\ninterface BarcodeScannerProps {\n /** Called when a code is successfully read */\n onDecode: (value: string) => void;\n /** Optional flag to hide the scanner */\n active?: boolean;\n}\n\nexport const BarcodeScanner = ({ onDecode, active = true }: BarcodeScannerProps) => {\n const [error, setError] = useState<string | null>(null);\n const { ref } = useZxing({\n onDecodeResult: (result) => {\n onDecode(result.getText());\n },\n onError: (e: unknown) => {\n const errorMessage = e instanceof Error ? e.message : 'Camera error';\n setError(errorMessage);\n },\n });\n\n if (!active) return null;\n\n return (\n <div style={{ position: 'relative', width: '100%', maxWidth: '400px' }}>\n <video\n ref={ref as React.LegacyRef<HTMLVideoElement>}\n style={{ width: '100%', borderRadius: '8px' }}\n playsInline\n muted\n />\n {error && (\n <p style={{ color: 'red', marginTop: '0.5rem' }}>\n {error}\n </p>\n )}\n </div>\n );\n};\n\n","import { useState, useEffect } from 'react';\nimport type { AuthService } from '../../services/auth.service';\nimport type { RelayService } from '../../services/relay.service';\nimport type { AuthState } from '../../types/auth';\nimport type { ProfileMetadata, FollowEntry } from '../../types/nostr';\nimport { sanitizeText } from '../../utils/sanitize';\nimport { isValidHttpUrl } from '../../utils/validation';\nimport { BarcodeScanner } from './BarcodeScanner';\nimport './Membership.css';\n\ninterface ProfileWithPubkey extends ProfileMetadata {\n pubkey: string;\n}\n\ninterface MembershipPageProps {\n authService: AuthService;\n relayService: RelayService;\n publicKey: string | null;\n onUnauthenticated?: () => void;\n}\n\nconst TrustInfo = () => (\n <section className=\"trust-info\">\n <h2>Building Trust with Verifiable Relationship Credentials</h2>\n <p>\n Beyond the Personhood Credential, members can create <strong>Verifiable Relationship\n Credentials (VRCs)</strong>. A VRC is issued directly between two members – for example,\n by scanning a QR‑code at a meetup – and certifies a first‑hand trust link.\n </p>\n <p>\n Each VRC becomes a node in a decentralized trust graph. When you add a member, the\n system records a <strong>role</strong> tag that ties the new\n participant to your existing graph, enabling permissionless access to network‑state\n resources while keeping the underlying data private.\n </p>\n <p>\n The graph grows organically: trusted authorities issue PHCs, and members continuously\n enrich the network with peer‑generated VRCs, creating a resilient, scalable web of\n verified participants.\n </p>\n </section>\n);\n\nexport function MembershipPage({\n authService,\n relayService,\n publicKey,\n onUnauthenticated,\n}: MembershipPageProps) {\n const [isLoading, setIsLoading] = useState(false);\n const [isSaving, setIsSaving] = useState(false);\n const [saveMessage, setSaveMessage] = useState<string | null>(null);\n const [searchQuery, setSearchQuery] = useState('');\n const [profiles, setProfiles] = useState<ProfileWithPubkey[]>([]);\n const [members, setMembers] = useState<FollowEntry[]>([]);\n const [memberProfiles, setMemberProfiles] = useState<Map<string, ProfileMetadata>>(new Map());\n const [showScanner, setShowScanner] = useState(false);\n\n useEffect(() => {\n if (!publicKey) {\n if (onUnauthenticated) {\n onUnauthenticated();\n }\n return;\n }\n\n loadFollowList();\n }, [publicKey]);\n\n useEffect(() => {\n if (members.length > 0) {\n loadMemberProfiles();\n }\n }, [members]);\n\n const handleDecoded = (decoded: string) => {\n setSearchQuery(decoded);\n setShowScanner(false);\n handleSearch();\n };\n\n const loadFollowList = async () => {\n if (!publicKey) return;\n\n setIsLoading(true);\n try {\n const followList = await relayService.fetchFollowList(publicKey);\n setMembers(followList);\n } catch (error) {\n console.error('Failed to load follow list:', error);\n setSaveMessage('Failed to load membership list');\n } finally {\n setIsLoading(false);\n }\n };\n\n const loadMemberProfiles = async () => {\n if (members.length === 0) return;\n\n try {\n const pubkeys = members.map((m) => m.pubkey);\n const profilesMap = await relayService.fetchMultipleProfiles(pubkeys);\n setMemberProfiles(profilesMap);\n } catch (error) {\n console.error('Failed to load member profiles:', error);\n }\n };\n\n const handleSearch = async () => {\n if (!searchQuery.trim()) {\n setIsLoading(true);\n try {\n const profilesMap = await relayService.queryProfiles([], 50);\n const profilesList: ProfileWithPubkey[] = [];\n profilesMap.forEach((profile, pubkey) => {\n profilesList.push({ ...profile, pubkey });\n });\n setProfiles(profilesList);\n } catch (error) {\n console.error('Failed to query profiles:', error);\n setSaveMessage('Failed to search profiles');\n } finally {\n setIsLoading(false);\n }\n return;\n }\n\n const trimmedQuery = searchQuery.trim();\n if (trimmedQuery.length !== 64 || !/^[0-9a-fA-F]+$/.test(trimmedQuery)) {\n setSaveMessage('Invalid pubkey format. Must be 64 hex characters.');\n setTimeout(() => setSaveMessage(null), 3000);\n return;\n }\n\n setIsLoading(true);\n try {\n const profilesMap = await relayService.queryProfiles([trimmedQuery], 1);\n const profilesList: ProfileWithPubkey[] = [];\n profilesMap.forEach((profile, pubkey) => {\n profilesList.push({ ...profile, pubkey });\n });\n setProfiles(profilesList);\n if (profilesList.length === 0) {\n setSaveMessage('No profile found for this pubkey');\n setTimeout(() => setSaveMessage(null), 3000);\n }\n } catch (error) {\n console.error('Failed to query profile:', error);\n setSaveMessage('Failed to search profile');\n } finally {\n setIsLoading(false);\n }\n };\n\n const handleAddMember = async (pubkey: string) => {\n if (!publicKey) return;\n\n if (members.some((m) => m.pubkey === pubkey)) {\n setSaveMessage('User is already a member');\n setTimeout(() => setSaveMessage(null), 3000);\n return;\n }\n\n setIsSaving(true);\n try {\n const newMembers: FollowEntry[] = [\n ...members,\n {\n pubkey,\n },\n ];\n\n await relayService.publishFollowList(publicKey, newMembers, (event) =>\n authService.signEvent(event)\n );\n\n setMembers(newMembers);\n setSaveMessage('Member added successfully!');\n setTimeout(() => setSaveMessage(null), 3000);\n } catch (error) {\n console.error('Failed to add member:', error);\n setSaveMessage('Failed to add member. Please try again.');\n } finally {\n setIsSaving(false);\n }\n };\n\n const handleRemoveMember = async (pubkey: string) => {\n if (!publicKey) return;\n\n setIsSaving(true);\n try {\n const newMembers = members.filter((m) => m.pubkey !== pubkey);\n\n await relayService.publishFollowList(publicKey, newMembers, (event) =>\n authService.signEvent(event)\n );\n\n setMembers(newMembers);\n setSaveMessage('Member removed successfully!');\n setTimeout(() => setSaveMessage(null), 3000);\n } catch (error) {\n console.error('Failed to remove member:', error);\n setSaveMessage('Failed to remove member. Please try again.');\n } finally {\n setIsSaving(false);\n }\n };\n\n const getProfileDisplayName = (profile: ProfileMetadata, pubkey: string): string => {\n return profile.display_name || profile.name || pubkey.slice(0, 16) + '...';\n };\n\n const formatPubkey = (pubkey: string): string => {\n return `${pubkey.slice(0, 8)}...${pubkey.slice(-8)}`;\n };\n\n if (isLoading && members.length === 0) {\n return (\n <div className=\"membership-container\">\n <div className=\"loading-state\">\n <div className=\"spinner\"></div>\n <p>Loading membership list...</p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"membership-container\">\n <div className=\"membership-card\">\n <h1>Membership Management</h1>\n <TrustInfo />\n <p className=\"membership-description\">\n Query applicants and manage the membership list.\n </p>\n\n {saveMessage && (\n <div\n className={`save-message ${\n saveMessage.includes('Error') || saveMessage.includes('Failed')\n ? 'error'\n : 'success'\n }`}\n >\n {saveMessage}\n </div>\n )}\n\n <div className=\"search-section\">\n <h2>Add Member</h2>\n <div className=\"search-form\">\n <input\n type=\"text\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n onKeyPress={(e) => e.key === 'Enter' && handleSearch()}\n placeholder=\"Enter pubkey or leave empty for recent profiles\"\n className=\"search-input\"\n disabled={isLoading}\n />\n <button\n onClick={handleSearch}\n className=\"search-button\"\n disabled={isLoading || isSaving}\n >\n {isLoading ? 'Searching...' : 'Search'}\n </button>\n <button\n onClick={() => setShowScanner((prev) => !prev)}\n className=\"scanner-toggle\"\n disabled={isLoading || isSaving}\n >\n {showScanner ? 'Close Scanner' : 'Scan QR'}\n </button>\n </div>\n\n {showScanner && (\n <div style={{ marginTop: '1rem' }}>\n <BarcodeScanner onDecode={handleDecoded} active={true} />\n <p style={{ fontSize: '0.85rem', marginTop: '0.5rem' }}>\n Point at QR‑code containing a pubkey\n </p>\n </div>\n )}\n\n {profiles.length > 0 && (\n <div className=\"profiles-list\">\n <h3>Search Results</h3>\n {profiles.map((profile) => {\n const isMember = members.some((m) => m.pubkey === profile.pubkey);\n return (\n <div key={profile.pubkey} className=\"profile-item\">\n <div className=\"profile-info\">\n {profile.picture && isValidHttpUrl(profile.picture) && (\n <img\n src={profile.picture}\n alt={sanitizeText(getProfileDisplayName(profile, profile.pubkey))}\n className=\"profile-avatar\"\n loading=\"lazy\"\n referrerPolicy=\"no-referrer\"\n onError={(event) => {\n event.currentTarget.style.display = 'none';\n }}\n />\n )}\n <div className=\"profile-details\">\n <div className=\"profile-name\">\n {sanitizeText(getProfileDisplayName(profile, profile.pubkey))}\n </div>\n <div className=\"profile-pubkey\">{formatPubkey(profile.pubkey)}</div>\n {profile.about && (\n <div className=\"profile-about\">{sanitizeText(profile.about)}</div>\n )}\n </div>\n </div>\n <button\n onClick={() =>\n isMember\n ? handleRemoveMember(profile.pubkey)\n : handleAddMember(profile.pubkey)\n }\n className={`member-button ${isMember ? 'remove' : 'add'}`}\n disabled={isSaving}\n >\n {isMember ? 'Remove' : 'Add Member'}\n </button>\n </div>\n );\n })}\n </div>\n )}\n </div>\n\n <div className=\"members-section\">\n <h2>Current Members ({members.length})</h2>\n {members.length === 0 ? (\n <p className=\"empty-message\">No members yet. Add members from search results above.</p>\n ) : (\n <div className=\"members-list\">\n {members.map((member) => {\n const profile = memberProfiles.get(member.pubkey);\n return (\n <div key={member.pubkey} className=\"member-item\">\n <div className=\"profile-info\">\n {profile?.picture && isValidHttpUrl(profile.picture) && (\n <img\n src={profile.picture}\n alt={sanitizeText(getProfileDisplayName(profile || {}, member.pubkey))}\n className=\"profile-avatar\"\n loading=\"lazy\"\n referrerPolicy=\"no-referrer\"\n onError={(event) => {\n event.currentTarget.style.display = 'none';\n }}\n />\n )}\n <div className=\"profile-details\">\n <div className=\"profile-name\">\n {profile\n ? sanitizeText(getProfileDisplayName(profile, member.pubkey))\n : formatPubkey(member.pubkey)}\n </div>\n <div className=\"profile-pubkey\">{formatPubkey(member.pubkey)}</div>\n {profile?.about && (\n <div className=\"profile-about\">{sanitizeText(profile.about)}</div>\n )}\n {member.petname && (\n <div className=\"profile-petname\">Name: {sanitizeText(member.petname)}</div>\n )}\n </div>\n </div>\n <button\n onClick={() => handleRemoveMember(member.pubkey)}\n className=\"member-button remove\"\n disabled={isSaving}\n >\n Remove\n </button>\n </div>\n );\n })}\n </div>\n )}\n </div>\n </div>\n </div>\n );\n}\n\n","import { useState, useEffect } from 'react';\nimport type { AuthService } from '../../services/auth.service';\nimport type { RelayService } from '../../services/relay.service';\nimport type { AuthState } from '../../types/auth';\nimport type { ProfileMetadata } from '../../types/nostr';\nimport { sanitizeText } from '../../utils/sanitize';\nimport { isValidPubkey, isValidRoleTag } from '../../utils/validation';\nimport './Profile.css';\n\ninterface ProfilePageProps {\n authService: AuthService;\n relayService: RelayService;\n publicKey: string | null;\n onUnauthenticated?: () => void;\n onSuccess?: () => void;\n onRoleSuggestion?: (about: string) => Promise<string | null>;\n}\n\nconst PersonhoodInfo = () => (\n <section className=\"personhood-info\">\n <p>\n Network‑state members receive a <strong>Personhood Credential (PHC)</strong> from a trusted authority.\n The PHC attests that the holder is a unique, real individual. Because the credential lives\n in your passport, you can present a <em>zero‑knowledge proof</em> that you are verified.\n </p>\n <p>\n In this form we compare the name you enter with the name disclosed by your verified\n passport proof. If the two match, the profile will be saved as your business card credential together with a\n passport tag that references the PHC's unique identifier.\n </p>\n </section>\n);\n\nconst MAX_NAME_LENGTH = 100;\nconst MAX_ABOUT_LENGTH = 1000;\nconst MAX_URL_LENGTH = 2048;\n\nexport function ProfilePage({\n authService,\n relayService,\n publicKey,\n onUnauthenticated,\n onSuccess,\n onRoleSuggestion,\n}: ProfilePageProps) {\n const [isLoading, setIsLoading] = useState(false);\n const [isSaving, setIsSaving] = useState(false);\n const [saveMessage, setSaveMessage] = useState<string | null>(null);\n const [suggestedRole, setSuggestedRole] = useState<string | null>(null);\n const [isGettingRole, setIsGettingRole] = useState(false);\n const [formData, setFormData] = useState<ProfileMetadata>({\n name: '',\n display_name: '',\n about: '',\n picture: '',\n website: '',\n });\n\n useEffect(() => {\n if (!publicKey) {\n if (onUnauthenticated) {\n onUnauthenticated();\n }\n return;\n }\n\n loadProfile();\n }, [publicKey]);\n\n const loadProfile = async () => {\n if (!publicKey) return;\n\n setIsLoading(true);\n try {\n const [profile, roleTag] = await Promise.all([\n relayService.fetchProfile(publicKey),\n relayService.fetchProfileRoleTag(publicKey),\n ]);\n \n if (profile) {\n setFormData({\n name: sanitizeText(profile.name || ''),\n display_name: sanitizeText(profile.display_name || ''),\n about: sanitizeText(profile.about || ''),\n picture: profile.picture || '',\n website: profile.website || '',\n });\n }\n \n if (roleTag) {\n setSuggestedRole(roleTag);\n }\n } catch (error) {\n console.error('Failed to load profile:', error);\n } finally {\n setIsLoading(false);\n }\n };\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n if (!publicKey) return;\n\n setIsSaving(true);\n setSaveMessage(null);\n\n try {\n const tags: string[][] = [];\n if (!isValidPubkey(publicKey)) {\n throw new Error('Invalid public key format');\n }\n\n // Suggest role based on \"about\" field if it has content and callback provided\n if (formData.about && formData.about.trim().length > 0 && onRoleSuggestion) {\n setIsGettingRole(true);\n try {\n const roleSuggestion = await onRoleSuggestion(formData.about);\n const candidate = roleSuggestion ? roleSuggestion.trim() : '';\n if (candidate && isValidRoleTag(candidate)) {\n tags.push(['role', candidate]);\n setSuggestedRole(candidate);\n } else {\n setSuggestedRole(null);\n }\n } catch (error) {\n console.error('Failed to get role suggestion:', error);\n setSuggestedRole(null);\n } finally {\n setIsGettingRole(false);\n }\n } else {\n setSuggestedRole(null);\n }\n\n const profileEvent = {\n kind: 0,\n content: JSON.stringify({\n ...formData,\n name: sanitizeText(formData.name || ''),\n display_name: sanitizeText(formData.display_name || ''),\n about: sanitizeText(formData.about || ''),\n }),\n created_at: Math.floor(Date.now() / 1000),\n tags,\n };\n\n const follows = {\n kind: 3,\n content: \"\",\n created_at: Math.floor(Date.now() / 1000),\n tags: [\n (() => {\n const followTag: string[] = ['p', publicKey];\n const relayUrl = relayService.getRelays()[0];\n if (relayUrl) {\n followTag.push(relayUrl);\n }\n if (formData.name) {\n followTag.push(formData.name);\n }\n return followTag;\n })(),\n ],\n };\n\n const signedProfile = await authService.signEvent(profileEvent);\n const signedFollows = await authService.signEvent(follows);\n\n await relayService.publishEvent(signedProfile);\n await relayService.publishEvent(signedFollows);\n\n setSaveMessage('Profile saved successfully!');\n setTimeout(() => {\n setSaveMessage(null);\n }, 3000);\n\n if (onSuccess) {\n onSuccess();\n }\n } catch (error) {\n console.error('Failed to save profile:', error);\n setSaveMessage('Failed to save profile. Please try again.');\n } finally {\n setIsSaving(false);\n }\n };\n\n const handleChange = (\n e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>\n ) => {\n const { name, value } = e.target;\n setFormData((prev) => ({\n ...prev,\n [name]: value,\n }));\n };\n\n if (isLoading) {\n return (\n <div className=\"profile-container\">\n <div className=\"loading-state\">\n <div className=\"spinner\"></div>\n <p>Loading profile...</p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"profile-container\">\n <div className=\"profile-card\">\n <h1>Profile Setup</h1>\n <PersonhoodInfo />\n\n <form onSubmit={handleSubmit} className=\"profile-form\">\n <div className=\"form-group\">\n <label htmlFor=\"name\">First Name</label>\n <input\n type=\"text\"\n id=\"name\"\n name=\"name\"\n value={formData.name || ''}\n onChange={handleChange}\n placeholder=\"Your username\"\n maxLength={MAX_NAME_LENGTH}\n />\n </div>\n\n <div className=\"form-group\">\n <label htmlFor=\"display_name\">Last Name</label>\n <input\n type=\"text\"\n id=\"display_name\"\n name=\"display_name\"\n value={formData.display_name || ''}\n onChange={handleChange}\n placeholder=\"Your Last Name\"\n maxLength={MAX_NAME_LENGTH}\n />\n </div>\n\n <div className=\"form-group\">\n <label htmlFor=\"about\">About</label>\n <textarea\n id=\"about\"\n name=\"about\"\n value={formData.about || ''}\n onChange={handleChange}\n placeholder=\"Tell us about yourself\"\n rows={4}\n maxLength={MAX_ABOUT_LENGTH}\n />\n </div>\n\n <div className=\"form-group\">\n <label htmlFor=\"picture\">Profile Picture URL</label>\n <input\n type=\"url\"\n id=\"picture\"\n name=\"picture\"\n value={formData.picture || ''}\n onChange={handleChange}\n placeholder=\"https://example.com/avatar.jpg\"\n maxLength={MAX_URL_LENGTH}\n />\n </div>\n\n <div className=\"form-group\">\n <label htmlFor=\"website\">LinkedIn</label>\n <input\n type=\"url\"\n id=\"website\"\n name=\"website\"\n value={formData.website || ''}\n onChange={handleChange}\n placeholder=\"https://linkedin.com/example\"\n maxLength={MAX_URL_LENGTH}\n />\n </div>\n\n {saveMessage && (\n <div className={`save-message ${saveMessage.includes('Error') ? 'error' : 'success'}`}>\n {saveMessage}\n </div>\n )}\n\n {(isGettingRole || suggestedRole) && (\n <div className=\"role-tag-container\">\n {isGettingRole ? (\n <>\n <div className=\"role-tag-label\">Getting AI suggestion...</div>\n <div className=\"role-tag-loading\">\n <div className=\"role-tag-spinner\"></div>\n </div>\n </>\n ) : suggestedRole ? (\n <>\n <div className=\"role-tag-label\">AI Suggested Role:</div>\n <div className=\"role-tag\">\n {sanitizeText(suggestedRole)}\n </div>\n </>\n ) : null}\n </div>\n )}\n\n <div className=\"form-actions\">\n <button type=\"submit\" className=\"save-button\" disabled={isSaving}>\n {isSaving ? 'Saving...' : 'Save Profile'}\n </button>\n </div>\n </form>\n </div>\n </div>\n );\n}\n\n","import { useEffect } from 'react';\nimport type { AuthService } from '../services/auth.service';\nimport type { AuthState } from '../types/auth';\n\n/**\n * Hook to initialize auth state on app load\n */\nexport function useAuthInit(\n authService: AuthService,\n setAuthenticated: AuthState['setAuthenticated']\n): void {\n useEffect(() => {\n if (authService.hasKeyInfo()) {\n const keyInfo = authService.getCurrentKeyInfo();\n if (keyInfo) {\n setAuthenticated(keyInfo);\n }\n }\n }, [authService, setAuthenticated]);\n}\n\n","import { create } from 'zustand';\nimport type { NostrKeyInfo } from '../utils';\nimport type { AuthState } from '../types/auth';\n\nexport const createAuthStore = () => {\n return create<AuthState>((set) => ({\n isAuthenticated: false,\n publicKey: null,\n keyInfo: null,\n loginError: null,\n setAuthenticated: (keyInfo: NostrKeyInfo | null) => {\n set({\n isAuthenticated: !!keyInfo,\n publicKey: keyInfo?.pubkey || null,\n keyInfo,\n loginError: null,\n });\n },\n setLoginError: (error: string | null) => {\n set({ loginError: error });\n },\n logout: () => {\n set({\n isAuthenticated: false,\n publicKey: null,\n keyInfo: null,\n loginError: null,\n });\n },\n }));\n};\n\n// Default singleton instance\nexport const useAuthStore = createAuthStore();\n\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const require_index = require('./index.cjs');
|
|
2
|
-
let
|
|
2
|
+
let nostr_tools_pure = require("nostr-tools/pure");
|
|
3
3
|
|
|
4
4
|
//#region src/utils/utils.ts
|
|
5
5
|
/**
|
|
@@ -427,13 +427,16 @@ var NosskeyManager = class {
|
|
|
427
427
|
async createNostrKey(credentialId, options = {}) {
|
|
428
428
|
const { secret: sk, id: responseId } = await getPrfSecret(credentialId, this.#prfOptions);
|
|
429
429
|
if (sk.every((byte) => byte === 0)) throw new Error("Invalid PRF output: all zeros");
|
|
430
|
-
|
|
431
|
-
|
|
430
|
+
bytesToHex(sk);
|
|
431
|
+
const publicKey = (0, nostr_tools_pure.getPublicKey)(sk);
|
|
432
|
+
const keyInfo = {
|
|
432
433
|
credentialId: bytesToHex(credentialId || responseId),
|
|
433
434
|
pubkey: publicKey,
|
|
434
435
|
salt: STANDARD_SALT,
|
|
435
436
|
...options.username && { username: options.username }
|
|
436
437
|
};
|
|
438
|
+
if (this.#keyCache.isEnabled() && this.#keyCache.getCacheOptions().cacheOnCreation) this.#keyCache.setKey(keyInfo.credentialId, sk);
|
|
439
|
+
return keyInfo;
|
|
437
440
|
}
|
|
438
441
|
/**
|
|
439
442
|
* @param event Nostr
|
|
@@ -450,7 +453,12 @@ var NosskeyManager = class {
|
|
|
450
453
|
sk = prfSecret;
|
|
451
454
|
if (shouldUseCache) this.#keyCache.setKey(keyInfo.credentialId, sk);
|
|
452
455
|
}
|
|
453
|
-
const signedEvent =
|
|
456
|
+
const signedEvent = (0, nostr_tools_pure.finalizeEvent)({
|
|
457
|
+
kind: event.kind,
|
|
458
|
+
content: event.content,
|
|
459
|
+
created_at: event.created_at || Math.floor(Date.now() / 1e3),
|
|
460
|
+
tags: tags ? [...event.tags || [], ...tags] : event.tags || []
|
|
461
|
+
}, sk);
|
|
454
462
|
if (!shouldUseCache && clearMemory) this.#clearKey(sk);
|
|
455
463
|
return signedEvent;
|
|
456
464
|
}
|
|
@@ -525,4 +533,4 @@ Object.defineProperty(exports, 'isPrfSupported', {
|
|
|
525
533
|
return isPrfSupported;
|
|
526
534
|
}
|
|
527
535
|
});
|
|
528
|
-
//# sourceMappingURL=utils-
|
|
536
|
+
//# sourceMappingURL=utils-BUM1trwg.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils-BUM1trwg.cjs","names":["#cacheOptions","#cachedEntry","#clearCachedEntry","#scheduleExpiry","#getCachedKeyIfValid","#clearKey","#expiryTimer","#keyCache","#storageOptions","#prfOptions","#loadKeyInfoFromStorage","#currentKeyInfo","#saveKeyInfoToStorage","#clearKey"],"sources":["../src/utils/utils.ts","../src/utils/key-cache.ts","../src/utils/prf-handler.ts","../src/utils/nosskey.ts","../src/utils/crypto-utils.ts"],"sourcesContent":["/**\n * @packageDocumentation\n */\n\n/**\n * @param bytes\n * @returns\n */\nexport function bytesToHex(bytes: Uint8Array): string {\n const key = '0123456789abcdef';\n let hex = '';\n for (let i = 0; i < bytes.length; i++) {\n const firstNibble = bytes[i] >> 4;\n const secondNibble = bytes[i] & 15;\n hex += key[firstNibble] + key[secondNibble];\n }\n return hex;\n}\n\n/**\n * @param hex\n * @returns\n */\nexport function hexToBytes(hex: string): Uint8Array {\n const key = '0123456789abcdef';\n const bytes = [];\n let currentByte = 0;\n let highNibble = true;\n\n for (let i = 0; i < hex.length; i++) {\n const charValue = key.indexOf(hex[i].toLowerCase());\n if (charValue === -1) continue;\n\n if (highNibble) {\n currentByte = charValue << 4;\n highNibble = false;\n } else {\n currentByte += charValue;\n bytes.push(currentByte);\n highNibble = true;\n }\n }\n\n return new Uint8Array(bytes);\n}\n","/**\n * Key cache management for Nosskey\n * @packageDocumentation\n */\n\nimport type { KeyCacheOptions } from './types.js';\nimport { bytesToHex } from './utils.js';\n\n/**\n * Key cache entry with expiration time\n */\ninterface CacheEntry {\n id: string;\n sk: Uint8Array;\n expireAt: number;\n}\n\n/**\n * Key cache manager for managing temporary secret keys\n */\nexport class KeyCache {\n #cachedEntry: CacheEntry | null = null;\n\n #expiryTimer: NodeJS.Timeout | null = null;\n\n #cacheOptions: KeyCacheOptions = {\n enabled: false,\n timeoutMs: 5 * 60 * 1000,\n };\n\n /**\n * KeyCache\n * @param options\n */\n constructor(options?: Partial<KeyCacheOptions>) {\n if (options) {\n this.#cacheOptions = { ...this.#cacheOptions, ...options };\n }\n }\n\n /**\n * @param options\n */\n setCacheOptions(options: Partial<KeyCacheOptions>): void {\n if (Object.keys(options).length > 0 && this.#cachedEntry !== null) {\n this.clearAllCachedKeys();\n }\n\n this.#cacheOptions = { ...this.#cacheOptions, ...options };\n }\n\n getCacheOptions(): KeyCacheOptions {\n return { ...this.#cacheOptions };\n }\n\n isEnabled(): boolean {\n return this.#cacheOptions.enabled;\n }\n\n /**\n * @param credentialId\n * @param sk\n */\n setKey(credentialId: Uint8Array | string, sk: Uint8Array): void {\n if (!this.#cacheOptions.enabled) return;\n\n const id = typeof credentialId === 'string' ? credentialId : bytesToHex(credentialId);\n const timeout =\n this.#cacheOptions.timeoutMs !== undefined ? this.#cacheOptions.timeoutMs : 5 * 60 * 1000;\n const expireAt = Date.now() + timeout;\n\n this.#clearCachedEntry();\n\n this.#cachedEntry = {\n id,\n sk: new Uint8Array(sk),\n expireAt,\n };\n\n try {\n this.#scheduleExpiry();\n } catch (error) {\n this.#clearCachedEntry();\n throw error;\n }\n }\n\n /**\n * @param credentialId\n * @returns undefined\n */\n getKey(credentialId: Uint8Array | string): Uint8Array | undefined {\n if (!this.#cacheOptions.enabled) return undefined;\n\n const id = typeof credentialId === 'string' ? credentialId : bytesToHex(credentialId);\n return this.#getCachedKeyIfValid(id);\n }\n\n /**\n * @param credentialId\n */\n clearCachedKey(credentialId: Uint8Array | string): void {\n const id = typeof credentialId === 'string' ? credentialId : bytesToHex(credentialId);\n\n if (this.#cachedEntry && this.#cachedEntry.id === id) {\n this.#clearCachedEntry();\n }\n }\n\n clearAllCachedKeys(): void {\n this.#clearCachedEntry();\n }\n\n /**\n * @param credentialId\n * @returns undefined\n */\n #getCachedKeyIfValid(credentialId: string): Uint8Array | undefined {\n if (!this.#cachedEntry || this.#cachedEntry.id !== credentialId) {\n return undefined;\n }\n\n if (Date.now() < this.#cachedEntry.expireAt) {\n return this.#cachedEntry.sk;\n }\n\n this.#clearCachedEntry();\n return undefined;\n }\n\n #clearCachedEntry(): void {\n if (this.#cachedEntry) {\n this.#clearKey(this.#cachedEntry.sk);\n this.#cachedEntry = null;\n }\n\n if (this.#expiryTimer) {\n clearTimeout(this.#expiryTimer);\n this.#expiryTimer = null;\n }\n }\n\n #scheduleExpiry(): void {\n if (!this.#cachedEntry) return;\n\n const now = Date.now();\n const timeToExpiry = this.#cachedEntry.expireAt - now;\n\n if (timeToExpiry <= 0) {\n this.#clearCachedEntry();\n return;\n }\n\n this.#expiryTimer = setTimeout(() => {\n this.#clearCachedEntry();\n }, timeToExpiry + 1);\n }\n\n /**\n * @param key\n */\n #clearKey(key: Uint8Array): void {\n key?.fill?.(0);\n }\n}\n","/**\n * PRF (Pseudo-Random Function) handler for WebAuthn\n * @packageDocumentation\n */\n\nimport type { GetPrfSecretOptions, PasskeyCreationOptions } from './types.js';\n\nconst PRF_EVAL_INPUT = new TextEncoder().encode('nostr-pwk');\n\n/**\n * @returns PRF\n */\nexport async function isPrfSupported(): Promise<boolean> {\n try {\n const response = await navigator.credentials.get({\n publicKey: {\n challenge: crypto.getRandomValues(new Uint8Array(32)),\n allowCredentials: [],\n userVerification: 'required',\n extensions: { prf: { eval: { first: PRF_EVAL_INPUT } } },\n } as PublicKeyCredentialRequestOptions,\n });\n\n if (!response) return false;\n\n const assertion = response as unknown as {\n getClientExtensionResults: () => {\n prf?: {\n results?: {\n first?: ArrayBuffer;\n };\n };\n };\n };\n\n const res = assertion.getClientExtensionResults()?.prf?.results?.first;\n return !!res;\n } catch {\n return false;\n }\n}\n\n/**\n * @param options\n * @returns Credential\n */\nexport async function createPasskey(options: PasskeyCreationOptions = {}): Promise<Uint8Array> {\n // Node\n const rpName = options.rp?.name || (typeof location !== 'undefined' ? location.host : 'Nosskey');\n const rpId = options.rp?.id;\n const userName = options.user?.name || 'user@example.com';\n const userDisplayName = options.user?.displayName || 'Nosskey user';\n\n const credentialCreationOptions: CredentialCreationOptions = {\n publicKey: {\n rp: {\n name: rpName,\n id: rpId,\n },\n user: {\n id: crypto.getRandomValues(new Uint8Array(16)),\n name: userName,\n displayName: userDisplayName,\n },\n pubKeyCredParams: options.pubKeyCredParams || [{ type: 'public-key', alg: -7 }], // ES256\n authenticatorSelection: options.authenticatorSelection || {\n residentKey: 'required',\n userVerification: 'required',\n },\n challenge: crypto.getRandomValues(new Uint8Array(32)),\n extensions: options.extensions || { prf: {} }, // PRF拡張を要求\n } as PublicKeyCredentialCreationOptions,\n };\n const cred = (await navigator.credentials.create(\n credentialCreationOptions\n )) as PublicKeyCredential;\n\n return new Uint8Array(cred.rawId);\n}\n\n/**\n * ID\n * @param credentialId \n * @param options PRF(rpId、timeout、userVerification)\n * @returns PRF credentialID\n */\nexport async function getPrfSecret(\n credentialId?: Uint8Array,\n options?: GetPrfSecretOptions\n): Promise<{ secret: Uint8Array; id: Uint8Array }> {\n const allowCredentials = credentialId ? [{ type: 'public-key' as const, id: credentialId }] : [];\n\n const requestOptions: PublicKeyCredentialRequestOptions = {\n challenge: crypto.getRandomValues(new Uint8Array(32)),\n allowCredentials,\n userVerification: options?.userVerification || 'required',\n extensions: {\n prf: { eval: { first: PRF_EVAL_INPUT } },\n } as AuthenticationExtensionsClientInputs,\n };\n\n if (options?.rpId) {\n requestOptions.rpId = options.rpId;\n }\n if (options?.timeout) {\n requestOptions.timeout = options.timeout;\n }\n\n const response = await navigator.credentials.get({\n publicKey: requestOptions,\n });\n\n if (!response) {\n throw new Error('Authentication failed');\n }\n\n const assertion = response as unknown as {\n getClientExtensionResults: () => {\n prf?: {\n results?: {\n first?: ArrayBuffer;\n };\n };\n };\n };\n\n const secret = assertion.getClientExtensionResults()?.prf?.results?.first;\n if (!secret) {\n throw new Error('PRF secret not available');\n }\n\n // response credentialId\n const responseId = new Uint8Array((response as PublicKeyCredential).rawId);\n\n return {\n secret: new Uint8Array(secret),\n id: responseId,\n };\n}\n","import { finalizeEvent, getPublicKey } from 'nostr-tools/pure';\nimport { KeyCache } from './key-cache.js';\nimport { createPasskey, getPrfSecret, isPrfSupported } from './prf-handler.js';\nimport type {\n GetPrfSecretOptions,\n KeyCacheOptions,\n KeyOptions,\n NosskeyManagerLike,\n NosskeyManagerOptions,\n NostrEvent,\n NostrKeyInfo,\n NostrKeyStorageOptions,\n PasskeyCreationOptions,\n SignOptions,\n} from './types.js';\n/**\n * Nosskey class for Passkey-Derived Nostr Identity\n * @packageDocumentation\n */\nimport { bytesToHex, hexToBytes } from './utils.js';\n\n// salt(\"nostr-key\" UTF-8)\nconst STANDARD_SALT = '6e6f7374722d6b6579';\n\n/**\n * Nosskey - Passkey-Derived Nostr Keys\n */\nexport class NosskeyManager implements NosskeyManagerLike {\n #keyCache: KeyCache;\n\n // NostrKeyInfo\n #currentKeyInfo: NostrKeyInfo | null = null;\n\n // NostrKeyInfo\n #storageOptions: NostrKeyStorageOptions = {\n enabled: true,\n storageKey: 'nosskey_keyinfo',\n };\n\n // PRF\n #prfOptions: GetPrfSecretOptions = {};\n\n /**\n * NosskeyManager\n * @param options\n */\n constructor(options?: NosskeyManagerOptions) {\n // KeyCache\n this.#keyCache = new KeyCache(options?.cacheOptions);\n\n if (options?.storageOptions) {\n this.#storageOptions = { ...this.#storageOptions, ...options.storageOptions };\n }\n\n // option\n const userVerification = options?.prfOptions?.userVerification ?? 'required';\n if (options?.prfOptions) {\n this.#prfOptions = { ...options.prfOptions, userVerification };\n } else {\n this.#prfOptions = { userVerification };\n }\n\n // NostrKeyInfo\n if (this.#storageOptions.enabled) {\n const loadedKeyInfo = this.#loadKeyInfoFromStorage();\n if (loadedKeyInfo) {\n this.#currentKeyInfo = loadedKeyInfo;\n }\n }\n }\n\n /**\n * NostrKeyInfo\n * @param options\n */\n setStorageOptions(options: Partial<NostrKeyStorageOptions>): void {\n this.#storageOptions = { ...this.#storageOptions, ...options };\n\n if (options.enabled === false) {\n this.clearStoredKeyInfo();\n }\n }\n\n /**\n * NostrKeyInfo\n */\n getStorageOptions(): NostrKeyStorageOptions {\n return { ...this.#storageOptions };\n }\n\n /**\n * NostrKeyInfo\n * @param keyInfo NostrKeyInfo\n */\n setCurrentKeyInfo(keyInfo: NostrKeyInfo): void {\n this.#currentKeyInfo = keyInfo;\n\n if (this.#storageOptions.enabled) {\n void this.#saveKeyInfoToStorage(keyInfo);\n }\n }\n\n /**\n * NostrKeyInfo\n */\n getCurrentKeyInfo(): NostrKeyInfo | null {\n // NostrKeyInfo\n if (!this.#currentKeyInfo && this.#storageOptions.enabled) {\n this.#currentKeyInfo = this.#loadKeyInfoFromStorage();\n }\n return this.#currentKeyInfo;\n }\n\n /**\n * NostrKeyInfo\n * @returns NostrKeyInfo\n */\n hasKeyInfo(): boolean {\n if (this.#currentKeyInfo) {\n return true;\n }\n\n if (this.#storageOptions.enabled) {\n const loadedKeyInfo = this.#loadKeyInfoFromStorage();\n if (loadedKeyInfo) {\n this.#currentKeyInfo = loadedKeyInfo;\n return true;\n }\n }\n\n return false;\n }\n\n /**\n * NostrKeyInfo\n * @param keyInfo NostrKeyInfo\n */\n async #saveKeyInfoToStorage(keyInfo: NostrKeyInfo): Promise<void> {\n if (!this.#storageOptions.enabled) return;\n\n const storage =\n this.#storageOptions.storage || (typeof localStorage !== 'undefined' ? localStorage : null);\n\n if (!storage) return;\n\n const key = this.#storageOptions.storageKey || 'nosskey_keyinfo';\n storage.setItem(key, JSON.stringify(keyInfo));\n }\n\n /**\n * NostrKeyInfo\n */\n #loadKeyInfoFromStorage(): NostrKeyInfo | null {\n if (!this.#storageOptions.enabled) return null;\n\n const storage =\n this.#storageOptions.storage || (typeof localStorage !== 'undefined' ? localStorage : null);\n\n if (!storage) return null;\n\n const key = this.#storageOptions.storageKey || 'nosskey_keyinfo';\n const data = storage.getItem(key);\n\n if (!data) return null;\n\n try {\n return JSON.parse(data) as NostrKeyInfo;\n } catch (e) {\n console.error('Failed to parse stored NostrKeyInfo', e);\n return null;\n }\n }\n\n /**\n * NostrKeyInfo\n */\n clearStoredKeyInfo(): void {\n const storage =\n this.#storageOptions.storage || (typeof localStorage !== 'undefined' ? localStorage : null);\n\n if (!storage) return;\n\n const key = this.#storageOptions.storageKey || 'nosskey_keyinfo';\n storage.removeItem(key);\n\n // NostrKeyInfo\n this.#currentKeyInfo = null;\n }\n\n /**\n * NIP-07\n * NostrKeyInfo\n */\n async getPublicKey(): Promise<string> {\n const keyInfo = this.getCurrentKeyInfo();\n if (!keyInfo) {\n throw new Error('No current NostrKeyInfo set');\n }\n return keyInfo.pubkey;\n }\n\n /**\n * NIP-07\n * NostrKeyInfo\n * @param event Nostr\n */\n async signEvent(event: NostrEvent): Promise<NostrEvent> {\n const keyInfo = this.getCurrentKeyInfo();\n if (!keyInfo) {\n throw new Error('No current NostrKeyInfo set');\n }\n return this.signEventWithKeyInfo(event, keyInfo);\n }\n\n /**\n * @param options\n */\n setCacheOptions(options: Partial<KeyCacheOptions>): void {\n this.#keyCache.setCacheOptions(options);\n }\n\n getCacheOptions(): KeyCacheOptions {\n return this.#keyCache.getCacheOptions();\n }\n\n /**\n * @param credentialId\n */\n clearCachedKey(credentialId: Uint8Array | string): void {\n this.#keyCache.clearCachedKey(credentialId);\n }\n\n clearAllCachedKeys(): void {\n this.#keyCache.clearAllCachedKeys();\n }\n\n /**\n * @param options\n * @returns Credential\n */\n async createPasskey(options: PasskeyCreationOptions = {}): Promise<Uint8Array> {\n return createPasskey({\n rp: {\n id: this.#prfOptions.rpId,\n name: this.#prfOptions.rpId,\n },\n authenticatorSelection: {\n userVerification: this.#prfOptions.userVerification,\n },\n ...options,\n });\n }\n\n /**\n * PRF NostrKeyInfo\n * @param credentialId\n * @param options\n */\n async createNostrKey(credentialId?: Uint8Array, options: KeyOptions = {}): Promise<NostrKeyInfo> {\n const { secret: sk, id: responseId } = await getPrfSecret(credentialId, this.#prfOptions);\n\n // secp256k1\n if (sk.every((byte) => byte === 0)) {\n throw new Error('Invalid PRF output: all zeros');\n }\n\n // HEX\n const skHex = bytesToHex(sk);\n\n const publicKey = getPublicKey(sk);\n\n // NostrKeyInfo\n const keyInfo: NostrKeyInfo = {\n credentialId: bytesToHex(credentialId || responseId),\n pubkey: publicKey,\n salt: STANDARD_SALT, // salt\n ...(options.username && { username: options.username }), // username\n };\n\n // Optional early caching based on cacheOnCreation setting\n if (this.#keyCache.isEnabled() && this.#keyCache.getCacheOptions().cacheOnCreation) {\n this.#keyCache.setKey(keyInfo.credentialId, sk);\n }\n\n return keyInfo;\n }\n\n /**\n * @param event Nostr\n * @param keyInfo NostrKeyInfo\n * @param options\n */\n async signEventWithKeyInfo(\n event: NostrEvent,\n keyInfo: NostrKeyInfo,\n options: SignOptions = {}\n ): Promise<NostrEvent> {\n const { clearMemory = true, tags } = options;\n\n const shouldUseCache = this.#keyCache.isEnabled();\n\n let sk: Uint8Array | undefined;\n\n if (shouldUseCache) {\n sk = this.#keyCache.getKey(keyInfo.credentialId);\n }\n\n if (!sk) {\n const { secret: prfSecret } = await getPrfSecret(\n hexToBytes(keyInfo.credentialId),\n this.#prfOptions\n );\n sk = prfSecret;\n\n if (shouldUseCache) {\n this.#keyCache.setKey(keyInfo.credentialId, sk);\n }\n }\n\n const eventToSign = {\n kind: event.kind,\n content: event.content,\n created_at: event.created_at || Math.floor(Date.now() / 1000),\n tags: tags ? [...(event.tags || []), ...tags] : event.tags || [],\n };\n\n const signedEvent = finalizeEvent(eventToSign, sk);\n\n if (!shouldUseCache && clearMemory) {\n this.#clearKey(sk);\n }\n\n return signedEvent;\n }\n\n /**\n * @param keyInfo NostrKeyInfo\n * @param credentialId NostrKeyInfoのcredentialId\n * @param options\n * @returns \n */\n async exportNostrKey(keyInfo: NostrKeyInfo, credentialId?: Uint8Array): Promise<string> {\n // NostrKeyInfo credentialId\n let usedCredentialId = credentialId;\n\n // credentialId NostrKeyInfo\n if (!usedCredentialId && keyInfo.credentialId) {\n usedCredentialId = hexToBytes(keyInfo.credentialId);\n }\n\n // PRF\n const { secret: sk } = await getPrfSecret(usedCredentialId, this.#prfOptions);\n\n // HEX\n const skHex = bytesToHex(sk);\n\n return skHex;\n }\n\n /**\n * PRF\n */\n async isPrfSupported(): Promise<boolean> {\n return isPrfSupported();\n }\n\n /**\n * @param key\n */\n #clearKey(key: Uint8Array): void {\n key?.fill?.(0);\n }\n}\n","/**\n * Cryptographic utilities for Nosskey\n * @packageDocumentation\n */\n\nconst INFO_BYTES = new TextEncoder().encode('nostr-pwk');\nconst AES_LENGTH = 256; // bits\n\n/**\n * PRF AES-GCM\n * @param secret PRF\n * @param salt\n * @returns AES-GCM\n */\nexport async function deriveAesGcmKey(secret: Uint8Array, salt: Uint8Array): Promise<CryptoKey> {\n const keyMaterial = await crypto.subtle.importKey('raw', secret, 'HKDF', false, ['deriveKey']);\n\n return crypto.subtle.deriveKey(\n { name: 'HKDF', hash: 'SHA-256', salt, info: INFO_BYTES },\n keyMaterial,\n { name: 'AES-GCM', length: AES_LENGTH },\n false,\n ['encrypt', 'decrypt']\n );\n}\n\n/**\n * AES-GCM\n * @param key\n * @param iv\n * @param plaintext\n * @returns\n */\nexport async function aesGcmEncrypt(key: CryptoKey, iv: Uint8Array, plaintext: Uint8Array) {\n const buf = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, plaintext);\n\n const bytes = new Uint8Array(buf);\n return {\n ciphertext: bytes.slice(0, -16),\n tag: bytes.slice(-16),\n };\n}\n\n/**\n * AES-GCM\n * @param key\n * @param iv\n * @param ct\n * @param tag\n * @returns\n */\nexport async function aesGcmDecrypt(\n key: CryptoKey,\n iv: Uint8Array,\n ct: Uint8Array,\n tag: Uint8Array\n): Promise<Uint8Array> {\n const buf = await crypto.subtle.decrypt(\n { name: 'AES-GCM', iv },\n key,\n new Uint8Array([...ct, ...tag])\n );\n return new Uint8Array(buf);\n}\n"],"mappings":"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"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as bytesToHex, i as isPrfSupported, n as createPasskey, o as hexToBytes, r as getPrfSecret, t as NosskeyManager } from "./utils-
|
|
1
|
+
import { a as bytesToHex, i as isPrfSupported, n as createPasskey, o as hexToBytes, r as getPrfSecret, t as NosskeyManager } from "./utils-DrZ-SwZX.mjs";
|
|
2
2
|
|
|
3
3
|
export { isPrfSupported };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { finalizeEvent, getPublicKey } from "nostr-tools/pure";
|
|
2
2
|
|
|
3
3
|
//#region src/utils/utils.ts
|
|
4
4
|
/**
|
|
@@ -426,13 +426,16 @@ var NosskeyManager = class {
|
|
|
426
426
|
async createNostrKey(credentialId, options = {}) {
|
|
427
427
|
const { secret: sk, id: responseId } = await getPrfSecret(credentialId, this.#prfOptions);
|
|
428
428
|
if (sk.every((byte) => byte === 0)) throw new Error("Invalid PRF output: all zeros");
|
|
429
|
-
|
|
430
|
-
|
|
429
|
+
bytesToHex(sk);
|
|
430
|
+
const publicKey = getPublicKey(sk);
|
|
431
|
+
const keyInfo = {
|
|
431
432
|
credentialId: bytesToHex(credentialId || responseId),
|
|
432
433
|
pubkey: publicKey,
|
|
433
434
|
salt: STANDARD_SALT,
|
|
434
435
|
...options.username && { username: options.username }
|
|
435
436
|
};
|
|
437
|
+
if (this.#keyCache.isEnabled() && this.#keyCache.getCacheOptions().cacheOnCreation) this.#keyCache.setKey(keyInfo.credentialId, sk);
|
|
438
|
+
return keyInfo;
|
|
436
439
|
}
|
|
437
440
|
/**
|
|
438
441
|
* @param event Nostr
|
|
@@ -449,7 +452,12 @@ var NosskeyManager = class {
|
|
|
449
452
|
sk = prfSecret;
|
|
450
453
|
if (shouldUseCache) this.#keyCache.setKey(keyInfo.credentialId, sk);
|
|
451
454
|
}
|
|
452
|
-
const signedEvent =
|
|
455
|
+
const signedEvent = finalizeEvent({
|
|
456
|
+
kind: event.kind,
|
|
457
|
+
content: event.content,
|
|
458
|
+
created_at: event.created_at || Math.floor(Date.now() / 1e3),
|
|
459
|
+
tags: tags ? [...event.tags || [], ...tags] : event.tags || []
|
|
460
|
+
}, sk);
|
|
453
461
|
if (!shouldUseCache && clearMemory) this.#clearKey(sk);
|
|
454
462
|
return signedEvent;
|
|
455
463
|
}
|
|
@@ -489,4 +497,4 @@ const INFO_BYTES = new TextEncoder().encode("nostr-pwk");
|
|
|
489
497
|
|
|
490
498
|
//#endregion
|
|
491
499
|
export { bytesToHex as a, isPrfSupported as i, createPasskey as n, hexToBytes as o, getPrfSecret as r, NosskeyManager as t };
|
|
492
|
-
//# sourceMappingURL=utils-
|
|
500
|
+
//# sourceMappingURL=utils-DrZ-SwZX.mjs.map
|