aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/changeid.ts11
-rw-r--r--lib/register.ts11
-rw-r--r--routes/users.ts5
3 files changed, 24 insertions, 3 deletions
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;
diff --git a/routes/users.ts b/routes/users.ts
index e8fdddd5..9f730b6f 100644
--- a/routes/users.ts
+++ b/routes/users.ts
@@ -313,7 +313,10 @@ export default {
}
}
- const fields = ["pubkey", "password", "username", "picture", "fresh"];
+ // No pubkey: nothing here proves the caller holds that key, and register()
+ // points `user:<pubkey>` at the new account. Nostr accounts come from
+ // /nostrAuth, which verifies a signed challenge first.
+ const fields = ["password", "username", "picture", "fresh"];
user = await register(pick(user, fields), ip);
const payload = { id: user.id };