From 10218ae86b464b8e3d29c27513eb5cc0554d044b Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Mon, 2 Nov 2026 13:19:16 -0700 Subject: users: ignore pubkey on signup, revoke a rekeyed account's nostr key Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01VMan6VN9xvnX1cgvV6hrUV --- lib/changeid.ts | 11 +++++++++-- lib/register.ts | 11 +++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/changeid.ts b/lib/changeid.ts index d4eca441..3c1f573d 100644 --- a/lib/changeid.ts +++ b/lib/changeid.ts @@ -16,9 +16,16 @@ export default async function changeid(un: string) { user.id = nid; user.password = "reset"; + // A rekey assumes the account's credentials are compromised, nostr key + // included. Drop the pubkey's login mapping rather than pointing it at the + // new id, and revoke the key so nostr login can't register a fresh account + // with it either. user.pubkey stays on the record so an admin can re-link. if (user.pubkey) { - await s(user.pubkey, nid); - await sa(user.pubkey, nid); + for (const k of [`user:${user.pubkey}`, user.pubkey]) { + await db.del(k); + await arc.del(k); + } + await db.sAdd("revoked:pubkeys", user.pubkey); } await s(`user:${un}`, nid); await sa(`user:${un}`, nid); diff --git a/lib/register.ts b/lib/register.ts index a9e9d7f7..bb9ce4b8 100644 --- a/lib/register.ts +++ b/lib/register.ts @@ -24,6 +24,17 @@ export default async (user, ip) => { if (isReserved(username)) fail("Invalid username"); if (username.includes("undefined")) fail("Invalid username"); + // Keys revoked by changeid() belong to compromised accounts, and a key that + // already resolves to a live account must not be re-pointed at a new one. + if (pubkey) { + const holder = await db.get(`user:${pubkey}`); + if ( + (await db.sIsMember("revoked:pubkeys", pubkey)) || + (holder && (await db.exists(`user:${holder.replace(/"/g, "")}`))) + ) + fail("Key in use by another account"); + } + const id = v4(); user.id = id; -- cgit v1.2.3