docs(verify): caveat that email proves control, not stable identity#276
Merged
umputun merged 1 commit intoMay 6, 2026
Merged
Conversation
The verify provider derives the local user id from the verified address
(ProviderName + "_" + HashID(address)). The confirmation round-trip
proves current control of the address at login time, but does not
guarantee a stable+unique identity over time -- the owner of an address
can change without the address changing (employer offboarding, recycled
free-mail handles, recycled domains). Any application that keys its own
records directly on the returned id will treat the new owner of an
address as the original user.
This is inherent to email-as-identity, not a bug in this library: the
verify flow has no upstream identifier (such as an OIDC sub) to fall
back on. The right place to address it is integrator-side -- map the
verified address to a server-side immutable user id at first verify --
but the property is non-obvious and easy to miss, so this commit calls
it out where readers will look:
- VerifyHandler doc comment in provider/verify.go (v1)
- VerifyHandler doc comment in v2/provider/verify.go
- new "Email-as-identity caveat" subsection under "Verified
authentication" in the root README
Documentation only; no behaviour change.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
paskal
approved these changes
May 4, 2026
umputun
approved these changes
May 6, 2026
Member
umputun
left a comment
There was a problem hiding this comment.
lgtm, thx.
for the IDFunc hook idea, pls open a separate issue so we can discuss the v2 design there.
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.
Summary
The
verifyprovider derives the local user id from the verified address:The confirmation round-trip proves current control of the address at login time, but it does not guarantee a stable+unique identity over time. The owner of an address can change without the address changing — employer offboarding, recycled free-mail handles, recycled domains. Any application that keys its own records directly on the returned id will treat the new owner of an address as the original user.
This is inherent to email-as-identity (the
verifyflow has no upstream identifier such as an OIDCsubto fall back on), so the right fix is integrator-side: map the verified address to a server-side immutable user id at first verify and key application records on that id. But the property is non-obvious and easy to miss, so this PR documents it where readers will look.Changes
Documentation only:
provider/verify.go—VerifyHandlerdoc comment gains an "Identity caveat" paragraph pointing at the README section.v2/provider/verify.go— same paragraph (v2 has no separate README, so the godoc link is the only in-package surface).README.md— new#### Email-as-identity caveatsubsection under Verified authentication, listing the failure modes (employer offboarding, lapsed free-mail accounts, recycled domains) and the integrator-side mitigation pattern. The tone matches the existing### Allowed redirect hostssection.No behaviour change.
Why now
Surfaced from a downstream identity-key audit (we work on a Paperclip company that hosts security-content reviews of OAuth/OIDC libraries). The verify flow's id derivation is technically correct for what
go-pkgz/authcontrols — there's nothing to fix in the library proper — but several integrators we surveyed had assumed the returned id was a stable account key rather than a live "currently controls this address" assertion. A documentation note seemed like the lowest-friction way to set expectations without changing behaviour or the API surface. Happy to drop, narrow, or split if you'd prefer.Future direction (not in this PR)
If you'd be open to a follow-up, an
IDFunchook onVerifyHandler(mirroring the one already ondirect.go) would let integrators substitute a registry-side immutable id without keeping a parallel mapping table. Filing this PR as docs-only first; happy to discuss a hook if you'd like.