passbolt/passbolt_api · error · BadRequestException
The authentication token is not valid.
Error message
The authentication token is not valid.
What it means
Thrown by assertAndConsumeToken in RecoverAbortService when getActiveNotExpiredOrFail finds no active, unexpired RECOVER token matching the given token id and user id. The abort-recovery endpoint therefore rejects the request because the token is unknown, already used, expired, or belongs to another user.
Solutions
- Request a fresh recovery (start the recover flow again) to obtain a new active token and use that link
- Confirm the token id in the URL matches the latest recover email and the user id matches the token's user_id
- Check authentication_tokens for the row: verify type = RECOVER, active = 1, expired date in the future
- Enable debug logging (Configure read('debug')) to see the underlying getActiveNotExpiredOrFail failure reason
Defensive patterns
Strategy: validation
Validate before calling
const t = await getTokenRow(tokenId); const valid = t && t.type === 'recover' && t.active && new Date(t.expired) > new Date() && t.user_id === userId;
Try / catch
try { await recoverAbort(userId, tokenId); }
catch (e) { if (isInvalidToken(e)) startNewRecoveryFlow(userId); else throw e; } Prevention
- Always use the token from the most recent recovery email
- Check token expiry before use
- Ensure the user id in the URL matches the token's owner
- Never replay an abort/complete call with the same token
When it happens
Trigger: POST /setup/recover/abort/{userId}/{tokenId} with an expired token, an already-consumed token, a token whose user_id does not match, or a malformed/nonexistent token id (wrong type).
Common situations: User clicking an old recover link after starting a newer recovery (older tokens invalidated/expired); token expired because the user waited too long; copying the wrong token id from logs; replaying an abort call twice.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- The authentication token has been expired.
- The authentication token is not valid.
- The authentication token does not exist or has been deleted.
- The authentication token has been expired.
- Ajax/Json request not supported.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/fbb8480be37adcf8.
Report an issue: GitHub.
Appendix: source
Thrown at src/Service/Setup/RecoverAbortService.php:83
/**
* Return the token or fail
*
* @param string $token token.token
* @param string $userId User ID
* @return void
* @throws \Cake\Http\Exception\BadRequestException if the token is missing, expired, invalid, or already consumed
*/
protected function assertAndConsumeToken(string $token, string $userId): void
{
try {
$tokenEntity = (new AuthenticationTokenGetService())
->getActiveNotExpiredOrFail($token, $userId, AuthenticationToken::TYPE_RECOVER);
} catch (NotFoundException $exception) {
if (Configure::read('debug')) {
Log::error('getActiveNotExpiredOrFail() failed: ' . $exception->getMessage());
}
throw new BadRequestException(__('The authentication token is not valid.'));
}
/** @var \App\Model\Table\AuthenticationTokensTable $authenticationTokensTable */
$authenticationTokensTable = $this->fetchTable('AuthenticationTokens');
if (!$authenticationTokensTable->setInactive($tokenEntity->token)) {
// Lost the concurrent-consume race.
throw new BadRequestException(__('The authentication token is not valid.'));
}
}
/**
* Return the user for matching the requesting id
*
* @param string $userId the user uuid
* @throws \Cake\Http\Exception\BadRequestException if the user id is not a valid uuid
* @throws \Cake\Http\Exception\BadRequestException if the user was deleted or has not completed the setup
* @return \App\Model\Entity\User
*/View on GitHub (pinned to 31c1bbc10f)