Found while wiring the verify route for #92 (see PR #99).
The problem
app/api/user/identities/verify/route.ts dispatches on identity.provider through a chain of if
branches — bluesky, github, mastodon:*, linkedin, twitter. For any provider string that
matches none of them:
- no branch runs, so nothing is verified
- the route still stamps
lastVerifiedAt
- it answers
200 {"success":true}
So an unrecognised provider is reported as a healthy, freshly-verified credential.
Why this is reachable, not theoretical
Mastodon identities are stored as mastodon:{instance}, and the branch matches that prefix. But a
bare "mastodon" string — which linkedIdentities can return — matches no branch and takes the
silent-success path. Any provider added to the product in future, or any row written before a naming
convention changed, lands there too.
Why the client cannot compensate
A 200 {"success":true} from a genuine verification and a 200 {"success":true} from the
no-branch-matched path are byte-identical. iOS has no way to distinguish them, so #99 does not try —
it takes the 200 at face value and reports the identity as connected. That means a user with such a
row is told their connection is fine when it has never been checked, which is worse than not
offering the check.
Ask
Make the unrecognised case explicit rather than silently successful. Either:
- Return an error —
400/422 with a distinguishable code (e.g. unsupported_provider), so
the client can say "can't check this one" rather than "verified"; or
- Return a success shape that says it didn't check — e.g.
{"success":true,"verified":false,"reason":"unsupported_provider"}
— and do not stamp lastVerifiedAt in that case.
(2) is friendlier to clients that already treat 200 as success. Either way, lastVerifiedAt should
not be written when no verification happened — it currently makes an unchecked credential look
recently confirmed.
Also worth checking while in there
- Should a bare
"mastodon" provider string exist at all? If not, that is a separate data-integrity
question about what linkedIdentities can return.
iOS follow-up
Once the contract is decided, Models/IdentityHealth.swift gains a third state alongside
connected / needsReconnect — something like "can't check" — and LinkedIdentitiesView renders it.
Small change; blocked on the decision above.
Found while wiring the verify route for #92 (see PR #99).
The problem
app/api/user/identities/verify/route.tsdispatches onidentity.providerthrough a chain ofifbranches —
bluesky,github,mastodon:*,linkedin,twitter. For any provider string thatmatches none of them:
lastVerifiedAt200 {"success":true}So an unrecognised provider is reported as a healthy, freshly-verified credential.
Why this is reachable, not theoretical
Mastodon identities are stored as
mastodon:{instance}, and the branch matches that prefix. But abare
"mastodon"string — whichlinkedIdentitiescan return — matches no branch and takes thesilent-success path. Any provider added to the product in future, or any row written before a naming
convention changed, lands there too.
Why the client cannot compensate
A
200 {"success":true}from a genuine verification and a200 {"success":true}from theno-branch-matched path are byte-identical. iOS has no way to distinguish them, so #99 does not try —
it takes the 200 at face value and reports the identity as connected. That means a user with such a
row is told their connection is fine when it has never been checked, which is worse than not
offering the check.
Ask
Make the unrecognised case explicit rather than silently successful. Either:
400/422with a distinguishable code (e.g.unsupported_provider), sothe client can say "can't check this one" rather than "verified"; or
{"success":true,"verified":false,"reason":"unsupported_provider"}— and do not stamp
lastVerifiedAtin that case.(2) is friendlier to clients that already treat 200 as success. Either way,
lastVerifiedAtshouldnot be written when no verification happened — it currently makes an unchecked credential look
recently confirmed.
Also worth checking while in there
"mastodon"provider string exist at all? If not, that is a separate data-integrityquestion about what
linkedIdentitiescan return.iOS follow-up
Once the contract is decided,
Models/IdentityHealth.swiftgains a third state alongsideconnected / needsReconnect — something like "can't check" — and
LinkedIdentitiesViewrenders it.Small change; blocked on the decision above.