Repository navigation
fix(auth): take the member email from the UserInfo endpoint - #2992
Merged
Merged
Conversation
better-auth 1.7 stopped putting user-record claims in the id_token and serves them from the UserInfo endpoint instead (OIDC Core section 5.4), so the callback now fetches GET /api/auth/oauth2/userinfo with the access token after the code exchange and resolves identity from that response. Email comes from userinfo only; a blank one fails the callback with :missing_email, keeping the previous commit's guard as the integrity floor so no member is ever keyed on `sub`. Name falls back from userinfo to the id_token name, then to the email address. The id_token remains the source of `sub` and `github_id` in `extra.raw_info`.
A userinfo transport failure, non-200 response, or malformed body now fails the callback with :userinfo_failed instead of :missing_email, so a degraded auth app is distinguishable from a member without an email. The rescue list gains the common Net::HTTP and OpenSSL transport errors (connection reset, unreachable host, SSL failure) that previously escaped to :unknown_error, and a valid-JSON non-object body counts as a request failure instead of raising.
mroderick
force-pushed
the
fix/userinfo-email
branch
from
October 7, 2026 16:48
49dcefd to
14f95b0
Compare
mroderick
marked this pull request as ready for review
October 7, 2026 17:50
This was referenced Oct 7, 2026
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.
Sign-in now resolves the member email and name from the OIDC UserInfo response instead of the id_token, so it keeps working with better-auth 1.7's sparse id_tokens and never keys a member on
sub.:missing_email, so no member is created from a non-email id. The id_tokenemailclaim is not consulted.:userinfo_failed, so a degraded auth app is distinguishable from a member without an email. Common Net::HTTP and OpenSSL transport errors are rescued into that path, and a non-object userinfo body counts as a request failure.subandgithub_idinextra.raw_info; the controller's returning-member resolution is unchanged.This continues the
:missing_emailguard from #2986, which is already on master: the guard stays as the integrity floor, and the email source moves to userinfo. Cleaning up members already keyed on better-auth user ids remains follow-up work. The auth app needs no claims code for this, so codebar/auth#83 can close.This is the same change as #2989, which merged into its stacked base branch instead of master; the change is unchanged and rebased onto master.
Review notes
Focus on the failure semantics and the email precedence. Userinfo is the only email source, even when the id_token carries one: a blank email there fails
:missing_email, a failed request fails:userinfo_failed. There is no fallback to the id_token email.Deliberately not done: a
sub-match guard between the id_token and the userinfo response (better-auth re-pinssub), checkingemail_verified(the auth app's verification policy is trusted as before), and shortening the userinfo timeout or capping concurrent callbacks under a degraded provider.Related: #2986, codebar/auth#83