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

  1. Complete the Azure sign-in when prompted instead of relying on an existing session, then retry.
  2. Set the prompt option to 'none' in Azure SSO settings if fresh authentication is not required for your policy.
  3. Retry the flow immediately after starting it so auth_time is after state creation.
  4. Check server clock synchronization (NTP) to rule out skew between passbolt and Azure timestamps.
  5. 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

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

Related errors


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)