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

  1. Re-issue the verify token through the proper server endpoint so the expiry is generated within the allowed window.
  2. Check the token payload contains a numeric expiry field (unix seconds) not exceeding maxTokenExpiry.
  3. Align client and server versions so expiry format matches what ValidateTokenService expects.
  4. 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

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


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)