Users signing in through the planner's codebar login could be dropped into a brand-new, empty account — no subscriptions, no roles — because the planner received an id_token without an email claim.
Timeline (2026-10-02)
- 10:01 UTC — auth release v64 deployed better-auth 1.6.27 → 1.7.5
- 13:18 UTC — first affected sign-in: planner created duplicate member 31450
- 15:06 UTC — second affected sign-in: planner created duplicate member 31452; reported by the member's organiser
- 16:34 UTC — mitigated by rolling back to the v63 code (release v65)
Mechanism
Two independent behaviours combine:
- better-auth 1.7 stopped including user-record claims (
email, name) in the id_token for sessions not created through a social provider (magic-link sessions). The claims hook receives the full user record and the correct scopes, but the claims are absent from the signed token.
- The planner resolves members by the id_token's
email claim and falls back to sub when the claim is absent (email = payload['email'] || payload['sub'] in lib/omniauth/strategies/codebar.rb). With the claim missing, the fallback creates a member whose email and name are the better-auth user id — an opaque 32-character id. Nothing matches the real account, so member:duplicates:detect finds nothing.
Fix
Follow-ups
Users signing in through the planner's codebar login could be dropped into a brand-new, empty account — no subscriptions, no roles — because the planner received an id_token without an
emailclaim.Timeline (2026-10-02)
Mechanism
Two independent behaviours combine:
email,name) in the id_token for sessions not created through a social provider (magic-link sessions). The claims hook receives the full user record and the correct scopes, but the claims are absent from the signed token.emailclaim and falls back tosubwhen the claim is absent (email = payload['email'] || payload['sub']inlib/omniauth/strategies/codebar.rb). With the claim missing, the fallback creates a member whose email and name are the better-auth user id — an opaque 32-character id. Nothing matches the real account, somember:duplicates:detectfinds nothing.Fix
customIdTokenClaimsalways emitsemailandnamefrom the user record for every session type; better-auth and @better-auth/oauth-provider bumped to 1.7.7; regression tests cover both the magic-link and linked-GitHub pathsFollow-ups
subfallback (fail loudly or fetch the userinfo endpoint instead)