passbolt/passbolt_api · error · BadRequestException
$e->getMessage() from OAuth2Exception during admin SSO…
Error message
$e->getMessage() from OAuth2Exception during admin SSO setup (dynamic), remapped to 400
What it means
During admin SSO setup stage2, assertStateCodeAndGetUac() exchanges the OAuth2 code with the provider and validates the state code. Any OAuth2Exception (token exchange failure, invalid code, wrong redirect_uri, provider error response) is remapped from a 500-level OAuth2Exception to a 400 BadRequestException carrying its dynamic message, with the original chained.
Solutions
- Re-read the message in the 400 response — it names the provider-side cause (e.g. invalid_grant, redirect_uri_mismatch) and fix the corresponding SSO draft setting.
- Restart the SSO setup from the admin settings screen to get a fresh authorization code; codes are single-use and expire in minutes.
- Verify client id, client secret, and redirect URI in the draft exactly match the provider's app registration.
- Check server outbound connectivity to the provider's token endpoint (firewall/proxy).
Defensive patterns
Strategy: try-catch
Validate before calling
if (!clientId || !clientSecret || !redirectUri) { throw new Error('SSO provider config incomplete before starting OAuth flow'); } Type guard
function isOAuth2Exception(e) { return e && (e.name === 'OAuth2Exception' || /^OAuth2/.test(String(e.class))); } Try / catch
try { await stage2AsAdmin(state, code); } catch (e) { if (e.status === 400 && /invalid_grant|redirect_uri_mismatch|invalid_client/.test(e.message)) { fixProviderSettings(e.message); restartSetup(); } else { throw e; } } Prevention
- Verify client id/secret/redirect URI against the provider app registration before setup
- Use each authorization code exactly once and within minutes of issuance
- Check outbound network access to the provider token endpoint
- Read the chained provider message to pinpoint the misconfiguration
When it happens
Trigger: Admin finishes the OAuth2 redirect and the controller calls assertStateCodeAndGetUac(); the provider rejects the code — expired/already-used authorization code, mismatched client_secret, wrong redirect URI, or misconfigured provider endpoints.
Common situations: Wrong client secret or redirect URI in the SSO draft settings; provider (Azure AD, Google, etc.) clock skew invalidating codes; user replaying the callback URL (code is single-use); network/DNS issues reaching the provider's token endpoint during setup.
Related errors
- $data['error'] (dynamic provider error)
- error
- error
- $exception->getMessage() from SsoSettingsGetService draft…
- Single sign-on failed. Provider error
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/509333d275515850.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/PassboltEe/Sso/src/Controller/AbstractSso2Stage2Controller.php:225
* @param \App\Service\Cookie\AbstractSecureCookieService $cookieService Cookie service
* @param \Passbolt\Sso\Model\Entity\SsoState $ssoState SSO state.
* @param string $code jwt
* @return void
*/
protected function stage2AsAdmin(AbstractSecureCookieService $cookieService, SsoState $ssoState, string $code): void
{
try {
// Get the draft settings
$settingsDto = (new SsoSettingsGetService())->getDraftByIdOrFail($ssoState->sso_settings_id, true);
} catch (Exception $exception) {
throw new BadRequestException($exception->getMessage(), 400, $exception);
}
try {
$service = $this->ssoServiceFactory($cookieService, $settingsDto);
$uac = $service->assertStateCodeAndGetUac($ssoState, $code, $this->User->ip(), $this->User->userAgent());
} catch (OAuth2Exception $e) { // Remap 500 error with 400 when admin is setting up SSO
throw new BadRequestException($e->getMessage(), 400, $e);
}
// Create authentication token for next step, e.g. activate settings
$ssoAuthToken = $service->createAuthTokenToActiveSettings($uac, $service->getSettings()->id);
$this->response = $this->getResponse()->withCookie($service->clearStateCookie());
$this->redirect(Router::url("/sso/login/dry-run/success?token={$ssoAuthToken->token}", true));
}
}
View on GitHub (pinned to 31c1bbc10f)