passbolt/passbolt_api · error · BadRequestException

The authentication token id is invalid.

Error message

The authentication token id is invalid.

What it means

BadRequestException thrown by AccountRecoveryContinueController::get() when the tokenId route parameter is missing or not a valid UUID. It follows the userId check and guards the authentication-token identifier format.

Solutions

  1. Regenerate the recovery link including the full authentication token UUID
  2. Validate the token id with Cake\Validation::uuid() client-side before navigating
  3. Confirm the email template renders the token id into the URL
  4. Check the route template accepts the token parameter and the client preserves it

Example fix

// before
$url = "/account-recovery/continue/{$userId}.json"; // token omitted
// after
$url = "/account-recovery/continue/{$userId}/{$accountRecoveryToken->id}.json";
Defensive patterns

Strategy: validation

Validate before calling

if (!isValidUuid(tokenId)) {
  throw new Error('authentication token id must be a UUID');
}

Type guard

function isValidUuid(value: unknown): value is string {
  return typeof value === 'string'
    && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
}

Try / catch

try {
  await accountRecoveryContinueService.get(userId, tokenId);
} catch (ApiError e) {
  if (e.message.includes('token id is invalid')) {
    showTokenLinkCorruptedScreen();
  }
}

Prevention

When it happens

Trigger: GET request to the account recovery continue endpoint with a missing, empty, or non-UUID token id, e.g. /account-recovery/continue/<user-uuid>.json or with 'null'/'0' as token id.

Common situations: Recovery email link missing the token segment; client stripping the token when redirecting; expired/rotated token id replaced with a placeholder in tests.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17). Data as JSON: /api/errors/221a72f385bd32b9. Report an issue: GitHub.

Appendix: source

Thrown at plugins/PassboltEe/AccountRecovery/src/Controller/AccountRecoveryContinue/AccountRecoveryContinueController.php:66

    /**
     * Render a page to continue the account recovery process
     *
     * @param string|null $userId User ID
     * @param string|null $tokenId Token ID
     * @param \Passbolt\Ee\Service\AccountRecoveryContinue\AccountRecoveryContinueAggregatorService $accountRecoveryContinueService Service instance.
     * @return void
     * @throws \Cake\Http\Exception\BadRequestException if the data provided is not valid
     */
    public function get(
        ?string $userId,
        ?string $tokenId,
        AccountRecoveryContinueAggregatorService $accountRecoveryContinueService
    ): void {
        if (!isset($userId) || !Validation::uuid($userId)) {
            throw new BadRequestException(__('The user id is invalid.'));
        }
        if (!isset($tokenId) || !Validation::uuid($tokenId)) {
            throw new BadRequestException(__('The authentication token id is invalid.'));
        }

        if ($this->getRequest()->is('json')) {
            // Do not allow logged in user to recover
            if ($this->User->role() !== Role::GUEST) {
                throw new ForbiddenException(__('Only guests are allowed to proceed with account recovery.'));
            }

            (new AccountRecoveryRequestGetService())->getOrFail($userId, $tokenId);

            $data = $accountRecoveryContinueService->get();

            $this->success(__('The operation was successful.'), $data);
        } else {
            $this->renderHtml();
        }
    }

View on GitHub (pinned to 31c1bbc10f)