{"record":{"id":"0af702ef7d7ab415","repo":"theonedev/onedev","slug":"unsolicited-oidc-authentication-response-0af702","errorCode":null,"errorMessage":"Unsolicited OIDC authentication response","messagePattern":"Unsolicited OIDC authentication response","errorType":"exception","errorClass":"AuthenticationException","httpStatus":null,"severity":"error","filePath":"server-plugin/server-plugin-sso-openid/src/main/java/io/onedev/server/plugin/sso/openid/OpenIdConnector.java","lineNumber":149,"sourceCode":"\tpublic void setClientSecret(String clientSecret) {\n\t\tthis.clientSecret = clientSecret;\n\t}\n\t\n\t@Override\n\tpublic SsoAuthenticated handleAuthResponse(String providerName) {\n\t\tHttpServletRequest request = (HttpServletRequest) RequestCycle.get().getRequest().getContainerRequest();\n\t\ttry {\n\t\t\tAuthenticationResponse authenticationResponse = AuthenticationResponseParser.parse(\n\t\t\t\t\tnew URI(request.getRequestURI() + \"?\" + request.getQueryString()));\n\t\t\tif (authenticationResponse instanceof AuthenticationErrorResponse) {\n\t\t\t\tthrow buildException(authenticationResponse.toErrorResponse().getErrorObject()); \n\t\t\t} else {\n\t\t\t\tAuthenticationSuccessResponse authenticationSuccessResponse = authenticationResponse.toSuccessResponse();\n\t\t\t\t\n\t\t\t\tString state = (String) Session.get().getAttribute(SESSION_ATTR_STATE);\n\t\t\t\t\n\t\t\t\tif (state == null || !state.equals(authenticationSuccessResponse.getState().getValue()))\n\t\t\t\t\tthrow new AuthenticationException(_T(\"Unsolicited OIDC authentication response\"));\n\t\t\t\t\n\t\t\t\tAuthorizationGrant codeGrant = new AuthorizationCodeGrant(\n\t\t\t\t\t\tauthenticationSuccessResponse.getAuthorizationCode(), getCallbackUri(providerName));\n\n\t\t\t\tClientID clientID = new ClientID(getClientId());\n\t\t\t\tcom.nimbusds.oauth2.sdk.auth.Secret clientSecret = new com.nimbusds.oauth2.sdk.auth.Secret(getClientSecret());\n\t\t\t\tClientAuthentication clientAuth = createTokenRequestAuthentication(clientID, clientSecret);\n\t\t\t\tTokenRequest tokenRequest = new TokenRequest(\n\t\t\t\t\t\tnew URI(getCachedProviderMetadata().getTokenEndpoint()), clientAuth, codeGrant, null);\n\t\t\t\t\n\t\t\t\tHTTPRequest httpRequest = tokenRequest.toHTTPRequest();\n\t\t\t\thttpRequest.setSSLSocketFactory(TrustCertsSSLSocketFactory.getDefault());\n\t\t\t\thttpRequest.setAccept(ContentType.APPLICATION_JSON.toString());\n\t\t\t\tHTTPResponse httpResponse = httpRequest.send();\n\t\t\t\tTokenResponse tokenResponse = parseOIDCTokenResponse(httpResponse);\n\t\t\t\t\n\t\t\t\tif (tokenResponse.indicatesSuccess()) \n\t\t\t\t\treturn processTokenResponse((OIDCTokenResponse)tokenResponse.toSuccessResponse());","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/theonedev/onedev/blob/d44925c47c37992c828ea673a5f9620539bc3ff2/server-plugin/server-plugin-sso-openid/src/main/java/io/onedev/server/plugin/sso/openid/OpenIdConnector.java#L131-L167","documentation":"Thrown by OpenIdConnector.handleAuthResponse when the OIDC callback's 'state' parameter does not match the state value stored in the session when the authentication request was initiated (or the session has no stored state at all). This is a CSRF/replay protection: the connector only accepts authorization responses it originated.","triggerScenarios":"The provider redirects back to the callback URL with a valid success response, but Session attribute SESSION_ATTR_STATE is null (session lost) or its value differs from authenticationSuccessResponse.getState() — e.g. stale callback, second login tab overwriting state, or a forged callback.","commonSituations":"User reloads or bookmarks the callback URL; two OIDC logins opened in parallel in the same browser session; load-balanced OneDev nodes without sticky sessions; proxies stripping the state query parameter; browser clock/cookie issues clearing the session.","solutions":["Start the OIDC login again from the sign-in button instead of reusing the callback URL.","Ensure cookies are enabled and the session survives the redirect to the provider and back.","Configure sticky sessions or shared session state when clustering OneDev.","Avoid running multiple concurrent SSO logins in the same browser profile; close extra tabs."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    auth = connector.handleAuthResponse(...);\n} catch (AuthenticationException e) {\n    if (e.getMessage().contains(\"Unsolicited OIDC authentication response\")) {\n        // state mismatch: redirect user to restart the OIDC login\n    }\n}","preventionTips":["Restart the login flow instead of reusing callback URLs.","Use sticky sessions behind load balancers.","Don't run concurrent OIDC logins in one browser session.","Ensure proxies don't strip the state parameter."],"tags":["oidc","oauth","sso","state-mismatch"],"backgroundTag":"oauth-state-mismatch","analyzedSha":"d44925c47c37992c828ea673a5f9620539bc3ff2","analyzedAt":"2026-09-06T07:18:27.995Z","contentChangedAt":"2026-09-06T07:18:27.995Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}