{"record":{"id":"33279846f6ad9443","repo":"spring-projects/spring-security","slug":"oauth2error-from-upstream-authorization-exception-332798","errorCode":null,"errorMessage":"OAuth2Error from upstream authorization exception (dynamic)","messagePattern":"OAuth2Error from upstream authorization exception \\(dynamic\\)","errorType":"exception","errorClass":"OAuth2AuthenticationException","httpStatus":null,"severity":"error","filePath":"oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2LoginAuthenticationProvider.java","lineNumber":122,"sourceCode":"\t\t// REQUIRED. OpenID Connect requests MUST contain the \"openid\" scope value.\n\t\tif (loginAuthenticationToken.getAuthorizationExchange()\n\t\t\t.getAuthorizationRequest()\n\t\t\t.getScopes()\n\t\t\t.contains(\"openid\")) {\n\t\t\t// This is an OpenID Connect Authentication Request so return null\n\t\t\t// and let OidcAuthorizationCodeAuthenticationProvider handle it instead\n\t\t\treturn null;\n\t\t}\n\t\tOAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthenticationToken;\n\t\ttry {\n\t\t\tauthorizationCodeAuthenticationToken = (OAuth2AuthorizationCodeAuthenticationToken) this.authorizationCodeAuthenticationProvider\n\t\t\t\t.authenticate(\n\t\t\t\t\t\tnew OAuth2AuthorizationCodeAuthenticationToken(loginAuthenticationToken.getClientRegistration(),\n\t\t\t\t\t\t\t\tloginAuthenticationToken.getAuthorizationExchange()));\n\t\t}\n\t\tcatch (OAuth2AuthorizationException ex) {\n\t\t\tOAuth2Error oauth2Error = ex.getError();\n\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), ex);\n\t\t}\n\t\tOAuth2AccessToken accessToken = authorizationCodeAuthenticationToken.getAccessToken();\n\t\tAssert.notNull(accessToken, \"accessToken cannot be null\");\n\t\tMap<String, Object> additionalParameters = authorizationCodeAuthenticationToken.getAdditionalParameters();\n\t\tOAuth2User oauth2User = this.userService.loadUser(new OAuth2UserRequest(\n\t\t\t\tloginAuthenticationToken.getClientRegistration(), accessToken, additionalParameters));\n\t\tAssert.notNull(oauth2User, \"oauth2User cannot be null\");\n\t\tCollection<GrantedAuthority> authorities = new HashSet<>(oauth2User.getAuthorities());\n\t\tCollection<GrantedAuthority> mappedAuthorities = new LinkedHashSet<>(\n\t\t\t\tthis.authoritiesMapper.mapAuthorities(authorities));\n\t\tmappedAuthorities.add(FactorGrantedAuthority.fromAuthority(AUTHORITY));\n\t\tOAuth2LoginAuthenticationToken authenticationResult = new OAuth2LoginAuthenticationToken(\n\t\t\t\tloginAuthenticationToken.getClientRegistration(), loginAuthenticationToken.getAuthorizationExchange(),\n\t\t\t\toauth2User, mappedAuthorities, accessToken, authorizationCodeAuthenticationToken.getRefreshToken());\n\t\tauthenticationResult.setDetails(loginAuthenticationToken.getDetails());\n\t\treturn authenticationResult;\n\t}\n","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2LoginAuthenticationProvider.java#L104-L140","documentation":"OAuth2LoginAuthenticationProvider.authenticate() delegates token exchange and user loading to downstream providers. When any of them throws an OAuth2AuthorizationException, it is re-wrapped as an OAuth2AuthenticationException carrying the upstream OAuth2Error (its toString() becomes the message). This is the normal failure funnel for Spring Security's OAuth2 login flow: the concrete cause is inside the wrapped error/exception.","triggerScenarios":"Thrown when the inner OAuth2AuthorizationCodeAuthenticationProvider (invoked via AuthenticationManager.authenticate(new OAuth2AuthorizationCodeAuthenticationToken(...))) fails during the authorization-code token exchange, or when userService.loadUser() fails, and an OAuth2AuthorizationException propagates to this catch block.","commonSituations":"Authorization server rejects the code (expired/already-used code), token endpoint returns an error JSON, token response is malformed, client authentication fails (bad client_secret or JWK), or the user-info endpoint returns an error. Frequently hit during local dev when redirect URI/state handling is wrong.","solutions":["Inspect the wrapped OAuth2AuthenticationException's getError() (errorCode, description, uri) and cause to identify the real upstream failure.","Verify the client registration (issuer URI, client-id/secret, redirect-uri, scopes) in your OAuth2Login configuration.","Check that the authorization request completed in one session (state/cookies preserved) and the code is not replayed.","If userinfo fails, hit the userinfo endpoint manually with the access token to see the raw error."],"exampleFix":"// before: opaque 500 on login\n// after: log the concrete OAuth2Error\ntry {\n    authenticationManager.authenticate(token);\n}\ncatch (OAuth2AuthenticationException ex) {\n    logger.warn(\"OAuth2 login failed: code={}, desc={}\", ex.getError().getErrorCode(), ex.getError().getDescription(), ex);\n    throw ex;\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { auth = authenticationManager.authenticate(token); } catch (OAuth2AuthenticationException ex) { OAuth2Error err = ex.getError(); log.warn(\"login failed: {} - {}\", err.getErrorCode(), err.getDescription(), ex); throw new LoginFailureException(err.getErrorCode(), ex); }","preventionTips":["Log ex.getError().getErrorCode() and getDescription() so the underlying cause is visible.","Keep cookies/session intact across the authorization redirect (state + code live in the session).","Test the full redirect flow with curl/browser devtools, not just unit tests.","Keep client registration values in sync with the provider's well-known configuration."],"tags":["oauth2","authentication","spring-security","authorization-code"],"backgroundTag":"oauth-token-exchange-failed","analyzedSha":"96852e8860138a482cb13d1479573f24ff6443c6","analyzedAt":"2026-09-10T23:25:23.477Z","contentChangedAt":"2026-09-10T23:25:23.477Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}