diff --git a/web/index.html b/web/index.html index c0f7425..465a73c 100644 --- a/web/index.html +++ b/web/index.html @@ -94,7 +94,7 @@

1 your server

Pin the key of the server that will hold your mail. Get it from its operator over a - channel you already trust — registering and fetching both refuse an unpinned server (SPEC.md §4).

+ channel you already trust — registering, recalling and fetching all refuse an unpinned server (SPEC.md §4).

@@ -111,6 +111,16 @@
+
+ already registered elsewhere? +

Restoring a seed brings back the identity, not the memory of what address it was + registered under in a different browser — the server was never asked. Recall it here: this + looks the address up and checks it resolves to this identity's key, without registering anything.

+
+ + +
+
diff --git a/web/js/app.js b/web/js/app.js index 7e89968..8e20b03 100644 --- a/web/js/app.js +++ b/web/js/app.js @@ -883,6 +883,37 @@ $("register-form").onsubmit = async (event) => { } }; +// Restoring a seed brings back the identity, not the memory of what address a +// *different* browser registered it under — "account" is local-only state, +// never asked of the server. This binds it without REGISTER: RESOLVE the +// address and require it name this exact key, so a typo or someone else's +// address can't silently misfile outgoing Reply-To / fetch under it. +$("recall-form").onsubmit = async (event) => { + event.preventDefault(); + const form = event.target; + try { + const me = store.identity(); + if (!me) throw new SmolError("no identity yet"); + const addr = parseAddress(form.address.value.trim()); + const opened = await connect(addr, { requirePin: true }); + let current; + try { + ({ identity: current } = await resolveOp(opened.session, addr.user)); + } finally { + opened.session.stream.close(); + } + if (!timingSafeEqual(current, me.publicKey)) + throw new SmolError(`${addr.short} resolves to a different key — not this identity`); + store.setAccount(addr); + status(`recalled ${addr.short}\nshare: ${addr.uri(me.publicKey)}`); + renderAccount(); + renderSettings(); + form.reset(); + } catch (err) { + status(err.message, true); + } +}; + async function rotateIdentity() { const me = store.identity(); const addr = accountAddress();