aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2025-01-09 23:10:25 -0800
committerAdam Soltys <[email protected]>2025-01-09 23:10:25 -0800
commit58a15601a1ffea775f80a9e7337b63f461b36b0c (patch)
tree3685f3d4a9de8a3d19637d642fa272c5547a7a76
parent6cda2011da9e7fc53b728dacd023271604574f99 (diff)
downloadcoinos-server-58a15601a1ffea775f80a9e7337b63f461b36b0c.tar.gz
coinos-server-58a15601a1ffea775f80a9e7337b63f461b36b0c.zip
pubkey challenge
-rw-r--r--routes/lnurl.ts4
-rw-r--r--routes/users.ts43
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);