// nip98.ts import { base64 } from "@scure/base"; // event.ts import { schnorr } from "@noble/curves/secp256k1"; import { sha256 } from "@noble/hashes/sha256"; import { bytesToHex } from "@noble/hashes/utils"; // utils.ts var utf8Decoder = new TextDecoder("utf-8"); var utf8Encoder = new TextEncoder(); // event.ts var verifiedSymbol = Symbol("verified"); function getBlankEvent(kind = 255 /* Blank */) { return { kind, content: "", tags: [], created_at: 0 }; } function serializeEvent(evt) { if (!validateEvent(evt)) throw new Error("can't serialize event with wrong or missing properties"); return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]); } function getEventHash(event) { let eventHash = sha256(utf8Encoder.encode(serializeEvent(event))); return bytesToHex(eventHash); } var isRecord = (obj) => obj instanceof Object; function validateEvent(event) { if (!isRecord(event)) return false; if (typeof event.kind !== "number") return false; if (typeof event.content !== "string") return false; if (typeof event.created_at !== "number") return false; if (typeof event.pubkey !== "string") return false; if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false; if (!Array.isArray(event.tags)) return false; for (let i = 0; i < event.tags.length; i++) { let tag = event.tags[i]; if (!Array.isArray(tag)) return false; for (let j = 0; j < tag.length; j++) { if (typeof tag[j] === "object") return false; } } return true; } function verifySignature(event) { if (typeof event[verifiedSymbol] === "boolean") return event[verifiedSymbol]; const hash = getEventHash(event); if (hash !== event.id) { return event[verifiedSymbol] = false; } try { return event[verifiedSymbol] = schnorr.verify(event.sig, hash, event.pubkey); } catch (err) { return event[verifiedSymbol] = false; } } // nip98.ts var _authorizationScheme = "Nostr "; async function getToken(loginUrl, httpMethod, sign, includeAuthorizationScheme = false) { if (!loginUrl || !httpMethod) throw new Error("Missing loginUrl or httpMethod"); const event = getBlankEvent(27235 /* HttpAuth */); event.tags = [ ["u", loginUrl], ["method", httpMethod] ]; event.created_at = Math.round(new Date().getTime() / 1e3); const signedEvent = await sign(event); const authorizationScheme = includeAuthorizationScheme ? _authorizationScheme : ""; return authorizationScheme + base64.encode(utf8Encoder.encode(JSON.stringify(signedEvent))); } async function validateToken(token, url, method) { const event = await unpackEventFromToken(token).catch((error) => { throw error; }); const valid = await validateEvent2(event, url, method).catch((error) => { throw error; }); return valid; } async function unpackEventFromToken(token) { if (!token) { throw new Error("Missing token"); } token = token.replace(_authorizationScheme, ""); const eventB64 = utf8Decoder.decode(base64.decode(token)); if (!eventB64 || eventB64.length === 0 || !eventB64.startsWith("{")) { throw new Error("Invalid token"); } const event = JSON.parse(eventB64); return event; } async function validateEvent2(event, url, method) { if (!event) { throw new Error("Invalid nostr event"); } if (!verifySignature(event)) { throw new Error("Invalid nostr event, signature invalid"); } if (event.kind !== 27235 /* HttpAuth */) { throw new Error("Invalid nostr event, kind invalid"); } if (!event.created_at) { throw new Error("Invalid nostr event, created_at invalid"); } if (Math.round(new Date().getTime() / 1e3) - event.created_at > 60) { throw new Error("Invalid nostr event, expired"); } const urlTag = event.tags.find((t) => t[0] === "u"); if (urlTag?.length !== 1 && urlTag?.[1] !== url) { throw new Error("Invalid nostr event, url tag invalid"); } const methodTag = event.tags.find((t) => t[0] === "method"); if (methodTag?.length !== 1 && methodTag?.[1].toLowerCase() !== method.toLowerCase()) { throw new Error("Invalid nostr event, method tag invalid"); } return true; } export { getToken, unpackEventFromToken, validateEvent2 as validateEvent, validateToken };