theonedev/onedev · error · AuthenticationException

Unsolicited OIDC authentication response

Error message

Unsolicited OIDC authentication response

What it means

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.

Source

Thrown at server-plugin/server-plugin-sso-openid/src/main/java/io/onedev/server/plugin/sso/openid/OpenIdConnector.java:149

	public void setClientSecret(String clientSecret) {
		this.clientSecret = clientSecret;
	}
	
	@Override
	public SsoAuthenticated handleAuthResponse(String providerName) {
		HttpServletRequest request = (HttpServletRequest) RequestCycle.get().getRequest().getContainerRequest();
		try {
			AuthenticationResponse authenticationResponse = AuthenticationResponseParser.parse(
					new URI(request.getRequestURI() + "?" + request.getQueryString()));
			if (authenticationResponse instanceof AuthenticationErrorResponse) {
				throw buildException(authenticationResponse.toErrorResponse().getErrorObject()); 
			} else {
				AuthenticationSuccessResponse authenticationSuccessResponse = authenticationResponse.toSuccessResponse();
				
				String state = (String) Session.get().getAttribute(SESSION_ATTR_STATE);
				
				if (state == null || !state.equals(authenticationSuccessResponse.getState().getValue()))
					throw new AuthenticationException(_T("Unsolicited OIDC authentication response"));
				
				AuthorizationGrant codeGrant = new AuthorizationCodeGrant(
						authenticationSuccessResponse.getAuthorizationCode(), getCallbackUri(providerName));

				ClientID clientID = new ClientID(getClientId());
				com.nimbusds.oauth2.sdk.auth.Secret clientSecret = new com.nimbusds.oauth2.sdk.auth.Secret(getClientSecret());
				ClientAuthentication clientAuth = createTokenRequestAuthentication(clientID, clientSecret);
				TokenRequest tokenRequest = new TokenRequest(
						new URI(getCachedProviderMetadata().getTokenEndpoint()), clientAuth, codeGrant, null);
				
				HTTPRequest httpRequest = tokenRequest.toHTTPRequest();
				httpRequest.setSSLSocketFactory(TrustCertsSSLSocketFactory.getDefault());
				httpRequest.setAccept(ContentType.APPLICATION_JSON.toString());
				HTTPResponse httpResponse = httpRequest.send();
				TokenResponse tokenResponse = parseOIDCTokenResponse(httpResponse);
				
				if (tokenResponse.indicatesSuccess()) 
					return processTokenResponse((OIDCTokenResponse)tokenResponse.toSuccessResponse());

View on GitHub (pinned to d44925c47c)

Solutions

  1. Start the OIDC login again from the sign-in button instead of reusing the callback URL.
  2. Ensure cookies are enabled and the session survives the redirect to the provider and back.
  3. Configure sticky sessions or shared session state when clustering OneDev.
  4. Avoid running multiple concurrent SSO logins in the same browser profile; close extra tabs.
Defensive patterns

Strategy: try-catch

Try / catch

try {
    auth = connector.handleAuthResponse(...);
} catch (AuthenticationException e) {
    if (e.getMessage().contains("Unsolicited OIDC authentication response")) {
        // state mismatch: redirect user to restart the OIDC login
    }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Related errors


AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06). Data as JSON: /api/errors/0af702ef7d7ab415. Report an issue: GitHub.