/auth/verify never returned the stored DID, so every login minted a throwaway one - #219
Merged
Merged
Conversation
/auth/verify hardcoded `did: null` in its response and never read the DID it
already held. Every login therefore looked like a first login:
1. client sees did:null, falls back to `did:temp:{subOrgId}`
2. client mints a BRAND NEW did:webvh and posts /api/user/updateDID
3. upsertUser refuses to overwrite an existing did:webvh (auth.ts:41), so the
write is a silent no-op — the endpoint still logs "updated successfully"
4. client keeps the discarded DID as its in-memory identity
So a fresh keypair was minted and thrown away on every single login, and the
client's DID disagreed with the database. That is why the stale-domain re-mint
never fired: by the time the hook ran, the client believed it was already on
boop.ad while users.did was still the trypoo.app DID, with all ~375 rows on it.
Confirmed on prod: after a sign-in, logs showed updateDID being called with a
new boop.ad DID, while users.did was unchanged and previewRemint still counted
every row against the old one.
Now returns the stored did (and displayName), so an existing user is recognised
and the client stops minting. A genuinely new account still has no DID and takes
the mint path unchanged.
Also guards the re-mint against a loop. Persisted auth state is restored on
reload without contacting the server, so a cached pre-migration DID would
re-trigger the hook on every load and walk the account to a new DID each time.
Two defences: the endpoint refuses to migrate an identity already on
WEBVH_DOMAIN, and the hook refreshes the cached auth state before reloading.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is why the re-mint still didn't run after #218.
The bug
/auth/verifyhardcodeddid: nullin its response and never read the DID it already held. So every login looked like a first login:did: null→ falls back todid:temp:{subOrgId}did:webvhand POSTs/api/user/updateDIDupsertUserrefuses to overwrite an existingdid:webvh(auth.ts:41) → silent no-op, while the endpoint still logs"DID updated successfully"Two consequences. A fresh keypair was minted and thrown away on every single login. And the client's DID silently disagreed with the database.
That second one is what defeated the re-mint: by the time the hook ran, the client believed it was already on
boop.ad, soisStaleDidDomainreturned false and it skipped — whileusers.didwas still thetrypoo.appDID with every row attached to it.Confirmed on prod
After a sign-in, logs showed:
while
findUserByEmailstill returned thetrypoo.appDID andpreviewRemintstill counted every row against it — 344 items, 18 lists, 7 sites, 7 publications, 2 categories, 1 bookmark.Fix
Return the stored
did(anddisplayName). An existing user is recognised and the client stops minting. A genuinely new account still has no DID and takes the mint path unchanged.Loop guard
With the DID now reported correctly, the hook fires — which exposes a second problem. Persisted auth state is restored on reload without contacting the server, so a cached pre-migration DID would re-trigger the hook every load and walk the account to a new DID each time.
Two independent defences:
WEBVH_DOMAIN. This holds regardless of client state.116 pass / 0 fail; both typechecks clean; lint clean on touched files.
After merge
Wait for Railway, not just Deploy Convex. Then sign in once per account.
users.didshould move andpreviewRemintagainst the old DID should drop to{}.🤖 Generated with Claude Code
Note
Fix
/auth/verifyto return the stored DID instead of always minting a new oneconvex/http.ts: After verifying a session, the handler now fetches the user byturnkeySubOrgIdand returns the storeddidanddisplayNameinstead of always returningnulland the email-derived display name.convex/userHttp.ts:POST /api/user/remintDidskips reminting when the user's existingdid:webvhdomain already matchesWEBVH_DOMAIN, returning the current DID with askippedreason.src/hooks/useDidDomainRemint.ts: After a successful remint, the hook updates the persisted auth state in storage with the new DID before reloading the page.Macroscope summarized 78f3d1a.