Skip to content

fix(auth): handle concurrent OAuth callback creating the same auth service - #2780

Merged
mroderick merged 2 commits into
masterfrom
fix/auth-callback-race
Aug 5, 2026
Merged

fix(auth): handle concurrent OAuth callback creating the same auth service#2780
mroderick merged 2 commits into
masterfrom
fix/auth-callback-race

Conversation

@mroderick

@mroderick mroderick commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Problem

A double-fired OAuth callback can pass the initial AuthService.find_by(provider:, uid:) lookup concurrently (both see nothing), then both enter the create branch and collide while member.save! saves the nested auth service. The unique (uid, provider) index (already live in prod) plus the auth_service uniqueness validation make one request error — RecordNotUnique on a true TOCTOU race, or RecordInvalid via the app validation when the duplicate is already visible. Result: an errored login request.

What this does

In auth_services_controller#create, wrap member.save! in a rescue of both RecordInvalid and RecordNotUnique, and on collision reuse the already-created auth service:

Verification

  • New request spec (spec/requests/auth_services_callback_spec.rb) deterministically exercises the rescue and asserts the loser reuses the winner's member/service and does not touch can_log_in.
  • Rebases cleanly onto current master (resolves the overlap with style(rubocop): resolve Rails/SkipsModelValidations todo entry #2778's can_log_in change).
  • Rubocop clean; request spec + signup feature specs (login / ToC / newsletter / joining): 20 examples, 0 failures.

Why a separate PR from #2779

The auth_services (uid, provider) index already shipped to prod in the first partial run of the unique-index migration, so this fix is independently urgent and not gated on #2779 — it gets focused auth review on its own clock.

@mroderick
mroderick marked this pull request as ready for review August 4, 2026 11:51
@mroderick
mroderick requested a review from olleolleolle August 4, 2026 12:14
…rvice

A double-fired OAuth callback can pass the initial service lookup concurrently,
then collide on the unique (uid, provider) index (or the auth_service uniqueness
validation) during member.save!, erroring the request. Rescue RecordInvalid and
RecordNotUnique and reuse the already-created auth_service, guarded by find_by!
so a non-race failure still surfaces. Toggle can_log_in only on actual creation
so the losing callback does not flip the winner flag back.
Comment thread spec/requests/auth_services_callback_spec.rb Outdated
Co-authored-by: Olle Jonsson <olle.jonsson@gmail.com>
@mroderick
mroderick merged commit 7f815eb into master Aug 5, 2026
9 checks passed
@mroderick
mroderick deleted the fix/auth-callback-race branch August 5, 2026 14:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants