diff options
| author | Adam Soltys <[email protected]> | 2025-01-09 23:10:25 -0800 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2025-01-09 23:10:25 -0800 |
| commit | 58a15601a1ffea775f80a9e7337b63f461b36b0c (patch) | |
| tree | 3685f3d4a9de8a3d19637d642fa272c5547a7a76 | |
| parent | 6cda2011da9e7fc53b728dacd023271604574f99 (diff) | |
| download | coinos-server-58a15601a1ffea775f80a9e7337b63f461b36b0c.tar.gz coinos-server-58a15601a1ffea775f80a9e7337b63f461b36b0c.zip | |
pubkey challenge
| -rw-r--r-- | routes/lnurl.ts | 4 | ||||
| -rw-r--r-- | routes/users.ts | 43 |
2 files changed, 36 insertions, 11 deletions
diff --git a/routes/lnurl.ts b/routes/lnurl.ts index 8aeeac6f..8fd7f1d6 100644 --- a/routes/lnurl.ts +++ b/routes/lnurl.ts @@ -94,7 +94,7 @@ export default { async lnurl(req, res) { const { params: { id }, - query: { amount, comment, nostr }, + query: { amount, comment, nostr, lnurl }, } = req; try { const uid = await g(`lnurl:${id}`); @@ -103,7 +103,7 @@ export default { let { username } = user; username = username.replace(/\s/g, "").toLowerCase(); - const memo = `Paying ${username}@${host}`; + const memo = `Paying ${username}@${host}` || comment; let metadata = JSON.stringify([ ["text/plain", memo], ["text/identifier", `${username}@${host}`], diff --git a/routes/users.ts b/routes/users.ts index f67721b7..298698f5 100644 --- a/routes/users.ts +++ b/routes/users.ts @@ -13,7 +13,7 @@ import upload from "$lib/upload"; import { bail, fail, getUser, pick } from "$lib/utils"; import whitelist from "$lib/whitelist"; import rpc from "@coinos/rpc"; -import { randomBytes } from "@noble/hashes/utils"; +import { bytesToHex, randomBytes } from "@noble/hashes/utils"; import { $ } from "bun"; import jwt from "jsonwebtoken"; import { getPublicKey, nip19, verifyEvent } from "nostr-tools"; @@ -212,7 +212,29 @@ export default { try { l("updating user", user.username); - const { confirm, password, pin, newpin, pubkey, username } = body; + const { confirm, password, pin, newpin, username } = body; + + let { pubkey } = body; + if (pubkey) { + const { challenge } = body; + const event = JSON.parse(body.event); + const c = await g(`challenge:${challenge}`); + if (!c) fail("Invalid or expired login challenge"); + + if ( + !verifyEvent(event) || + event.content !== challenge || + event.pubkey !== pubkey + ) + fail("Invalid signature or challenge mismatch."); + + pubkey = pubkey.replace(/\s*/g, ""); + if (pubkey.startsWith("npub")) pubkey = nip19.decode(pubkey).data; + if (pubkey.length !== 64) fail(`Invalid pubkey ${pubkey}`); + await db.del(`user:${user.pubkey}`); + user.pubkey = pubkey; + user.nsec = undefined; + } if (user.pin && !(pin === user.pin)) fail("Pin required"); if (typeof newpin !== "undefined" && newpin.length === 6) @@ -224,7 +246,11 @@ export default { let token; let oldname; - if (user.username.toLowerCase() !== username.toLowerCase() && exists) { + if ( + username && + user.username.toLowerCase() !== username.toLowerCase() && + exists + ) { err("username taken", username, user.username, exists.username); fail("Username taken"); } else if (username) { @@ -261,7 +287,6 @@ export default { "nsec", "picture", "prompt", - "pubkey", "push", "reserve", "seed", @@ -349,18 +374,18 @@ export default { async challenge(_, res) { const id = v4(); await db.set(`challenge:${id}`, id, { EX: 300 }); - res.send({ id }); + res.send({ challenge: id }); }, async nostrLogin(req, res) { try { - const { event, id, twofa } = req.body; + const { event, challenge, twofa } = req.body; const ip = req.headers["cf-connecting-ip"]; - const challenge = await g(`challenge:${id}`); + const c = await g(`challenge:${challenge}`); const { pubkey: key } = event; - if (!challenge) fail("Invalid or expired login challenge"); + if (!c) fail("Invalid or expired login challenge"); - if (!verifyEvent(event) || event.content !== id) + if (!verifyEvent(event) || event.content !== challenge) fail("Invalid signature or challenge mismatch."); let user = await getUser(key); |
