fix(auth): keep a stale bearer token from blocking sign-in - #57
Open
ExtraToast wants to merge 1 commit into
Open
ExtraToast wants to merge 1 commit into
ExtraToast wants to merge 1 commit into
Conversation
BearerTokenAuthenticationFilter runs before authorization, so a request carrying an undecodable Authorization header was answered with a bodyless 401 even on a permitAll path. A stale token in a browser profile made sign-in impossible while a private window worked. Public endpoints now answer on their own filter chain, which registers no resource server and keeps the CORS and CSRF configuration of the application chain.
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
Found while investigating a reported sign-in 401; it is not the cause of that report. The reported failure was
/api/oauth2/authorizeanswering 401 to a non-HTMLAccept(#59), followed by twelvesession-loginattempts that returned 400invalid-credentials. Seven days of Traefik access logs contain no 401 onsession-loginother than the probe that produced the table below. This PR closes a latent defect with the same symptom, not the incident.permitAlldoes not stopBearerTokenAuthenticationFilter.oauth2ResourceServersits on the application chain, so that filter runs on every path it covers, public ones included. It authenticates before authorization, so a request carrying anAuthorization: Bearerheader the decoder rejects fails there andHttpStatusEntryPoint(UNAUTHORIZED)answers before the controller runs.Measured against production
v0.7.0,POST /api/v1/auth/session-loginwith deliberately wrong credentials:Authorizationurn:problem-type:invalid-credentialsSESSION+XSRF-TOKENcookiesAuthorization: Bearer garbage.token.valuecontent-length: 0Wrong credentials are a
DomainException, whichGlobalExceptionHandlermaps to 400, and cookie state changes nothing — so any 401 from this endpoint can only come from a filter.register,login,refresh,confirm-emailand/api/v1/healthare affected identically.The spec makes this reachable rather than hypothetical:
client-spec/openapi/auth-api.jsondeclares a globalsecurity: [{bearerAuth: []}], so every generated client is told to attach a bearer token to sign-in.sessionLoginin the published TypeScript client carriessecurity: [{key: 'bearerAuth', scheme: 'bearer', type: 'http'}]for that reason. Any consumer configured with a token — a native client, a future UI — sends it to a login endpoint that then refuses the request.Fix
Public endpoints answer on
publicEndpointsSecurityFilterChainat order 2 (forward-auth moves to 3, application to 4). It matchesPUBLIC_ENDPOINTS, keeps the CORS source, the sameconfigureCsrfandCsrfCookieFilterso theXSRF-TOKENcookie is still issued, andpermitAll— but registers no resource server. The header is now inert where it carries no meaning. Bearer authentication on protected endpoints is untouched.Validation
SecurityFilterChainRoutingIntegrationTest.StaleBearerTokenOnPublicEndpoints:session-login(undecodable token, and a wrongly signed JWT),login,register,refresh,confirm-email,/api/v1/health. All seven were run against the unfixed chain first and all seven returned 401../gradlew :api:integrationTest— 214 tests, 0 failures, includingTokenSecurityIntegrationTestandSessionManagementIntegrationTest, which pin valid-bearer and session behaviour../gradlew :api:test :api:detektMain :api:detektTest :api:detektIntegrationTest— clean.Risk
permitAllon the application chain, and the CSRF configuration is the same object graph. The new chain's matcher is built fromPUBLIC_ENDPOINTSitself, so the two lists cannot drift.Authorizationheader to this host today — checked across auth-ui, home-portal, agents-ui and the legacy app-ui. The exposure is the published clients plus the spec's blanketsecurity, which is worth narrowing separately.Links