passbolt/passbolt_api · error · BadRequestException
Single sign-on failed. You must authenticate with Azure…
Error message
Single sign-on failed. You must authenticate with Azure again.
What it means
For Azure, when the settings' prompt option is not 'none', Passbolt checks the auth_time claim from the id_token: if the user's last Azure authentication happened before the SSO state was created, the sign-in did not occur during this flow, so a BadRequestException asking to re-authenticate with Azure is thrown.
Solutions
- Complete the Azure sign-in when prompted instead of relying on an existing session, then retry.
- Set the prompt option to 'none' in Azure SSO settings if fresh authentication is not required for your policy.
- Retry the flow immediately after starting it so auth_time is after state creation.
- Check server clock synchronization (NTP) to rule out skew between passbolt and Azure timestamps.
- Ensure the Azure app issues id_tokens including an accurate auth_time claim.
Example fix
// before prompt: 'login' but user silently reuses cached Azure session (auth_time older than state) // after prompt: 'none' // or force fresh sign-in at Azure so auth_time > state creation
Defensive patterns
Strategy: retry
Validate before calling
$claims = json_decode(base64_decode(explode('.', $idToken)[1]), true);
if (isset($claims['auth_time']) && $claims['auth_time'] < $ssoState->created->timestamp
&& $prompt !== 'none') {
// force a fresh Azure authentication before continuing
} Try / catch
try {
$uac = $service->assertStateCodeAndGetUac(...);
} catch (BadRequestException $e) {
if (str_contains($e->getMessage(), 'authenticate with Azure again')) {
// restart flow; user must sign in freshly at Azure
}
throw $e;
} Prevention
- Keep server clocks NTP-synchronized to avoid false auth_time comparisons.
- Set prompt to 'none' if fresh authentication is not required by your policy.
- Start and complete the SSO flow promptly; don't leave the state open for long.
- Sign out of stale Azure sessions if a cached session is silently reused.
When it happens
Trigger: assertAuthTime (called from assertResourceOwnerAgainstSsoState) finds authTime < ssoStateCreatedAt while prompt is set to login/consent/select_account — i.e. Azure returns an id_token from a cached session instead of a fresh authentication.
Common situations: Azure reuses an existing browser SSO session so no fresh authentication happens despite prompt != none; clock skew between passbolt server and Azure; state created long before the callback (user delayed completing the flow).
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Failed to public key properties from certificate
- Failed to read certificate
- Failed to read public key from certificate
- Invalid JWKS endpoint response. Keys missing.
- Invalid provider data. Expected Azure settings.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/41a83a0a1eb07d80.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/PassboltEe/Sso/src/Service/Sso/Azure/SsoAzureService.php:159
// HELPERS
/**
* @param int|null $authTime `auth_time` received from Azure. The value can be `null` when the claim is not added in
* the Azure AD admin console since it's an optional claim.
* @param int $ssoStateCreatedAt SSO state created timestamp.
* @return void
*/
private function assertAuthTime(?int $authTime, int $ssoStateCreatedAt): void
{
$ssoSettingsData = $this->getSettings()->getData()->toArray();
if ($ssoSettingsData['prompt'] === SsoSettingsAzureDataForm::PROMPT_NONE) {
return;
}
if ($authTime !== null && $authTime < $ssoStateCreatedAt) {
$msg = __('Single sign-on failed.') . ' ' . __('You must authenticate with Azure again.');
throw new BadRequestException($msg);
}
}
}
View on GitHub (pinned to 31c1bbc10f)