/** * NIP-98 HTTP Auth implementation * Generates authentication tokens for HTTP requests using Nostr events * See: https://github.com/nostr-protocol/nips/blob/master/98.md */ import type { EventTemplate } from 'nostr-tools' import { nostrRemoteSigner } from './nostrRemoteSigner' import { nostrService } from './nostr' /** * Generate NIP-98 authentication token for HTTP request * @param method HTTP method (GET, POST, etc.) * @param url Full URL of the request * @param payloadHash Optional SHA256 hash of the request body (for POST/PUT) * @returns Base64-encoded signed event token */ export async function generateNip98Token(method: string, url: string, payloadHash?: string): Promise { const pubkey = nostrService.getPublicKey() if (!pubkey) { throw new Error('Public key required for NIP-98 authentication. Please connect with a Nostr extension.') } // Parse URL to get components const urlObj = new URL(url) const path = urlObj.pathname + urlObj.search // Build event template for NIP-98 const tags: string[][] = [ ['u', urlObj.origin + path], ['method', method], ] // Add payload hash if provided (for POST/PUT requests) if (payloadHash) { tags.push(['payload', payloadHash]) } const eventTemplate: EventTemplate = { kind: 27235, // NIP-98 kind for HTTP auth created_at: Math.floor(Date.now() / 1000), tags: tags, content: '', } // Sign the event const signedEvent = await nostrRemoteSigner.signEvent(eventTemplate) if (!signedEvent) { throw new Error('Failed to sign NIP-98 authentication event') } // Encode event as base64 JSON const eventJson = JSON.stringify(signedEvent) const eventBytes = new TextEncoder().encode(eventJson) const base64Token = btoa(String.fromCharCode(...eventBytes)) return base64Token } /** * Check if NIP-98 authentication is available */ export function isNip98Available(): boolean { return nostrRemoteSigner.isAvailable() }