Files
SatsFaucet/backend/src/routes/auth.ts
Michaël 9fff2d427f Add public key (npub) login support
- Backend: POST /auth/login-npub endpoint, JWT carries login method
- Backend: Enforce Lightning address lock for npub logins in claim/quote
- Frontend: LoginModal supports npub via new endpoint, clear copy
- Frontend: Track loginMethod, lock LN address editing for npub users
- Handle missing Lightning address for npub users with helpful message

Made-with: Cursor
2026-02-27 17:56:15 -03:00

52 lines
1.8 KiB
TypeScript

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;