passbolt/passbolt_api · error · BadRequestException
The authentication token does not exist or has been deleted.
Error message
The authentication token does not exist or has been deleted.
What it means
Thrown by PingOneRecoverSuccessController::ssoRecoverSuccess when SsoAuthenticationTokenGetService::getActiveNotExpiredOrFail() cannot find an active, non-expired sso_auth_tokens record of type TYPE_SSO_RECOVER matching the token from the URL query. The RecordNotFoundException is wrapped in a BadRequestException with this message.
Solutions
- Restart the SSO recovery flow to obtain a fresh token link
- Verify the token query parameter is present and copied exactly from the email/link
- Do not reuse the success URL after the flow completed (token is single-use)
- Check sso_auth_tokens table for the token id and its active/expires fields when debugging
Example fix
// before: reused/consumed token GET /sso/recover/success/pingone?token=<already-used> // after GET /recover/start -> new link -> GET /sso/recover/success/pingone?token=<new-token>
Defensive patterns
Strategy: validation
Validate before calling
if (!token || typeof token !== 'string' || token.length < 8) {
throw new Error('Missing or malformed SSO recover token in URL.');
} Type guard
const hasToken = (q) => typeof q.token === 'string' && q.token.length > 0;
Try / catch
try {
await ssoRecoverSuccess(token);
} catch (e) {
if (e.message.includes('does not exist')) {
await restartRecoverFlow();
} else throw e;
} Prevention
- Use the token exactly as delivered in the link (no manual retyping)
- Treat tokens as single-use; never replay the success URL
- Restart the flow rather than debugging a consumed token
- Keep one browser/tab per recovery flow
When it happens
Trigger: Token query parameter missing, empty, mistyped, already consumed (single-use), or deleted; token belongs to a different type; recovery flow restarted so old token was replaced.
Common situations: User manually edits the callback URL; user completes recovery in one browser then reuses the link elsewhere; token consumed by an earlier duplicate callback request; database cleanup removed stale tokens.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Record not found in table "sso_auth_tokens"
- The authentication token does not exist.
- The authentication token has been expired.
- Ajax/Json request not supported.
- Ajax/Json request not supported.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/2a9d411453a3cf0c.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/PassboltEe/SsoRecover/src/Controller/PingOne/PingOneRecoverSuccessController.php:54
$this->Authentication->allowUnauthenticated(['ssoRecoverSuccess']);
}
/**
* @return void
*/
public function ssoRecoverSuccess(): void
{
if ($this->request->is('json')) {
throw new BadRequestException(__('Ajax/Json request not supported.'));
}
$this->User->assertNotLoggedIn();
$token = $this->getTokenFromUrlQuery();
try {
(new SsoAuthenticationTokenGetService())->getActiveNotExpiredOrFail($token, SsoState::TYPE_SSO_RECOVER);
} catch (RecordNotFoundException $e) {
throw new BadRequestException(
__('The authentication token does not exist or has been deleted.'),
null,
$e
);
} catch (CustomValidationException $e) {
throw new BadRequestException(
__('The authentication token has been expired.'),
null,
$e
);
}
$this->viewBuilder()
->setTheme('Passbolt/Sso')
->setLayout('default')
->setTemplatePath('success')
->setTemplate('stage3');
}View on GitHub (pinned to 31c1bbc10f)