passbolt/passbolt_api · error · InvalidVerifyTokenException
Invalid verify token expiry.
Error message
Invalid verify token expiry.
What it means
validateTokenExpiry throws InvalidVerifyTokenException when a verify token's expiry value is missing, non-numeric, or greater than the maximum allowed expiry (maxTokenExpiry). The verify token (used e.g. for account recovery/verify flows) must carry a numeric expiry within the permitted window, otherwise it is considered malformed/tampered.
Solutions
- Re-issue the verify token through the proper server endpoint so the expiry is generated within the allowed window.
- Check the token payload contains a numeric expiry field (unix seconds) not exceeding maxTokenExpiry.
- Align client and server versions so expiry format matches what ValidateTokenService expects.
- If max expiry config was tightened, force users to restart the verify flow rather than reusing old tokens.
Example fix
// before
const verifyToken = {user_id: uid, token: randomToken(), expiry: 'in two weeks'}; // non-numeric, unbounded
// after
const verifyToken = {user_id: uid, token: randomToken(), expiry: Math.floor(Date.now()/1000) + 10*60}; // numeric, within max Defensive patterns
Strategy: validation
Validate before calling
const expiry = verifyToken.expiry;
const maxExpiry = maxTokenExpiry;
if (typeof expiry !== 'number' || !Number.isFinite(expiry) || expiry > maxExpiry) throw new Error('verify token expiry must be a numeric timestamp within the allowed window'); Type guard
function hasValidExpiryShape(tok: unknown): tok is {expiry: number} {
return typeof tok === 'object' && tok !== null && typeof (tok as any).expiry === 'number' && Number.isFinite((tok as any).expiry);
} Try / catch
try {
$validationService->validateToken($verifyToken);
} catch (InvalidVerifyTokenException $e) {
throw new BadRequestException('Malformed verify token: regenerate it via the verify endpoint', 400, $e);
} Prevention
- Always let the server generate verify tokens; never hand-craft expiry values
- Keep client and server versions aligned on token payload format
- If max expiry config is tightened, invalidate outstanding tokens and restart flows
When it happens
Trigger: validateToken receiving a verify token whose stored/serialized expiry is absent, a non-numeric string, or a timestamp beyond the configured maximum lifetime — e.g. a client self-issuing a verify token with an arbitrarily long expiry.
Common situations: Tampered or hand-crafted verify token payloads; version drift where an older client wrote a different expiry format; server-side lowering of the max expiry after tokens were issued; corrupted token storage.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- The challenge is invalid. Validation Failed.
- The JWT private key should be at least
- Attempt to access an expired verify token.
- Invalid user ID format.
- Invalid verify token format.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/b5d03e50d534e456.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/PassboltCe/JwtAuthentication/src/Service/VerifyToken/VerifyTokenValidationService.php:67
/**
* Assert that the token expiry is valid and not set too far in the future.
*
* @param mixed $verifyTokenExpiry unix timestamp
* @return void
* @throws \Passbolt\JwtAuthentication\Error\Exception\VerifyToken\InvalidVerifyTokenException if the token is expired.
*/
protected function validateTokenExpiry(mixed $verifyTokenExpiry): void
{
$maxTokenExpiry = DateTime::now()
->modify('+' . Configure::read(self::VERIFY_TOKEN_EXPIRY_CONFIG_KEY))
->toUnixString();
if (
!isset($verifyTokenExpiry) ||
!is_numeric($verifyTokenExpiry) ||
$verifyTokenExpiry > $maxTokenExpiry
) {
throw new InvalidVerifyTokenException(__('Invalid verify token expiry.'));
}
if ($verifyTokenExpiry < time()) {
throw new ExpiredVerifyTokenAccessException(
__('Attempt to access an expired verify token.')
);
}
}
/**
* Assert verify token is a UUID
*
* @param mixed $verifyToken token
* @return void
* @throws \Passbolt\JwtAuthentication\Error\Exception\VerifyToken\InvalidVerifyTokenException if the format is not valid.
* @throws \Cake\ORM\Exception\PersistenceFailedException
*/
protected function validateFormat(mixed $verifyToken): void
{View on GitHub (pinned to 31c1bbc10f)