diff options
| author | Adam Soltys <[email protected]> | 2026-10-13 16:23:37 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-10-13 16:23:37 -0700 |
| commit | 12349761c03e769e1d54e878746a10d0a464b38d (patch) | |
| tree | c132cbe2b21de835370476491fb398aac19643f8 | |
| parent | d1a12bc0585aa583263362304218803ebf0afa9a (diff) | |
| download | coinos-server-12349761c03e769e1d54e878746a10d0a464b38d.tar.gz coinos-server-12349761c03e769e1d54e878746a10d0a464b38d.zip | |
/me only reports hasnsec now, so sessions that didn't start with a
password login had no way to reveal or sign with their key. POST /nsec
returns it after re-checking the password, under the login per-IP
failure limit.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01UjHzoBmW1iPpEe5qC2LDYB
| -rw-r--r-- | index.ts | 1 | ||||
| -rw-r--r-- | routes/users.ts | 33 |
2 files changed, 34 insertions, 0 deletions
@@ -183,6 +183,7 @@ app.get("/subscriptions", auth, users.subscriptions); app.post("/subscription", auth, users.subscription); app.post("/subscription/delete", auth, users.deleteSubscription); app.post("/password", auth, users.password); +app.post("/nsec", auth, users.nsec); app.post("/pin", auth, users.pin); app.post("/otpsecret", auth, users.otpsecret); app.get("/contacts/:limit?", auth, users.contacts); diff --git a/routes/users.ts b/routes/users.ts index aa4e4b11..684f361b 100644 --- a/routes/users.ts +++ b/routes/users.ts @@ -610,6 +610,39 @@ export default { } }, + // The encrypted nostr key, for clients that need to reveal or sign with it + // after a session that didn't start with a password login. Re-checking the + // password keeps a bearer token alone from fetching it (see /me). + async nsec(req, res) { + const { + body: { password }, + user, + } = req; + const ip = req.headers["cf-connecting-ip"] || req.socket.remoteAddress; + const ipFailKey = `ip:${ip}:login:fail`; + + try { + if (!user.nsec) fail("nsec not available"); + if ((await g(ipFailKey)) > 20) return res.code(429).send({}); + + let verified = false; + try { + if (user.password && password) + verified = await Bun.password.verify(password, user.password); + } catch (e) {} + + if (!verified) { + await db.incrBy(ipFailKey, 1); + if ((await db.ttl(ipFailKey)) < 0) await db.expire(ipFailKey, 600); + return res.code(401).send("invalid password"); + } + + res.send({ nsec: user.nsec }); + } catch (e) { + bail(res, e.message); + } + }, + async pin(req, res) { const { body: { pin }, |
