Repository navigation
Conversation
A second Sign In while the first is still redirecting replaces the first navigation. SimpleAuth issues a second authorization code, but the relying party has already spent its one-time state on the first one, so the second callback fails. OpenProject answers it with a bare "401 Unauthorized" and the user is left on a dead page even though the first login succeeded. The OIDC authorize page and the hosted /login page now ignore any submit after the first one, disable the button and show "Signing in...". A page restored from the back/forward cache accepts a new submit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Pressing Sign In a second time while the first sign-in is still redirecting signs the user in twice, and the second one fails at the relying party. The user ends up on an error page even though the first login succeeded.
What happens
Seen in production-like testing with OpenProject as the OIDC client. The OpenProject callback takes 1–2 s, and during that time the SimpleAuth page looks unchanged, so users click again (or press Enter, then click).
POST …/openid-connect/auth(first click)GET /auth/simpleauth/callback?code=A&state=SPOST …/openid-connect/auth(second click, same form)GET /auth/simpleauth/callback?code=B&state=SOpenProject's OIDC strategy reads the stored state with
session.delete('omniauth.state')and answers a mismatch with a bareRack::Response.new(['401 Unauthorized'], 401). Going back and signing in from the same page fails the same way, because that page still carries the spent state. Any client that treatsstateas one-time (as it should) reacts like this.The OIDC authorize page (
oidc.go) and the hosted/loginpage (hosted_login.go) share the same form markup, and neither guards against a second submit. The account page already disables its button while it submits (account.go).Change
Both login pages:
submitafter the first, which covers a second click, Enter, andrequestSubmit();Signing in..., so the user can see something is happening;pageshowwithpersisted(back/forward cache restore), reset the form so the user can sign in again.No server-side change. The snippet contains no
%, so thefmt.Sprintftemplate inhosted_login.gois unaffected.Verification
Chrome, against a local SimpleAuth whose redirect target is a callback that waits 2 s. The script clicks Sign In once, then again 0.5–0.7 s later:
App-local auth success)state, differentcodeSign InSigning in...requestSubmit()(Enter) 0.9 s laterSign Inenabled, form can be sent againUnit test:
TestLoginPagesSubmitOncerenders both pages and checks the guard is wired in. It fails on master and passes here. The behaviour itself is browser-side, hence the manual check above.go test ./...,go vetandgofmtare clean.🤖 Generated with Claude Code