import { Router, Request, Response } from "express"; import { nip19 } from "nostr-tools"; import { nip98Auth } from "../middleware/nip98.js"; import { signJwt, verifyJwt } from "../auth/jwt.js"; const router = Router(); /** Sign in with NIP-98 once; returns a JWT for subsequent requests. */ router.post("/login", nip98Auth, (req: Request, res: Response) => { const pubkey = req.nostr!.pubkey; const token = signJwt(pubkey, "nip98"); res.json({ token, pubkey, method: "nip98" }); }); /** Sign in with just an npub (no signature). Limited: cannot change Lightning address. */ router.post("/login-npub", (req: Request, res: Response) => { const raw = typeof req.body?.npub === "string" ? req.body.npub.trim() : ""; if (!raw) { res.status(400).json({ code: "invalid_npub", message: "npub is required." }); return; } try { const decoded = nip19.decode(raw); if (decoded.type !== "npub") { res.status(400).json({ code: "invalid_npub", message: "Expected an npub-encoded public key." }); return; } const pubkey = decoded.data as string; const token = signJwt(pubkey, "npub"); res.json({ token, pubkey, method: "npub" }); } catch { res.status(400).json({ code: "invalid_npub", message: "Invalid npub format." }); } }); /** Return current user from JWT (Bearer only). Used to restore session. */ router.get("/me", (req: Request, res: Response) => { const auth = req.headers.authorization; if (!auth?.startsWith("Bearer ")) { res.status(401).json({ code: "unauthorized", message: "Bearer token required." }); return; } const payload = verifyJwt(auth.slice(7).trim()); if (!payload) { res.status(401).json({ code: "invalid_token", message: "Invalid or expired token." }); return; } res.json({ pubkey: payload.pubkey, method: payload.method }); }); export default router;