{"record":{"id":"2b444f6bae26b7d2","repo":"passbolt/passbolt_api","slug":"the-authentication-token-is-not-valid-or-has-expired","errorCode":null,"errorMessage":"The authentication token is not valid or has expired.","messagePattern":"The authentication token is not valid or has expired\\.","errorType":"http","errorClass":"Cake\\Http\\Exception\\BadRequestException","httpStatus":400,"severity":"error","filePath":"plugins/PassboltEe/AccountRecovery/src/Service/AccountRecoveryRequests/AccountRecoveryRequestCreateService.php","lineNumber":173,"sourceCode":"     *\n     * @param string $userId the user uuid the token belongs to\n     * @throws \\Cake\\Http\\Exception\\BadRequestException if no authentication token was provided\n     * @throws \\Cake\\Http\\Exception\\BadRequestException if the authentication token is not a uuid\n     * @throws \\Cake\\Http\\Exception\\BadRequestException if the authentication token is expired or invalid\n     * @return \\App\\Model\\Entity\\AuthenticationToken\n     */\n    protected function getAndAssertToken(string $userId): AuthenticationToken\n    {\n        $token = $this->getData('authentication_token.token');\n        if (!isset($token)) {\n            throw new BadRequestException(__('An authentication token should be provided.'));\n        }\n\n        try {\n            $tokenEntity = (new AuthenticationTokenGetService())\n                ->getActiveNotExpiredOrFail($token, $userId, AuthenticationToken::TYPE_RECOVER);\n        } catch (NotFoundException $exception) {\n            throw new BadRequestException(__('The authentication token is not valid or has expired.'));\n        }\n\n        // Deactivate all previous active tokens\n        $this->AuthenticationTokens->updateQuery()\n            ->set(['active' => false])\n            ->where([\n                'id <>' => $tokenEntity->id,\n                'active' => true,\n                'type' => AuthenticationToken::TYPE_RECOVER,\n                'user_id' => $userId,\n            ])\n            ->execute();\n\n        return $tokenEntity;\n    }\n\n    /**\n     * @param array $data user provided data","sourceCodeStart":155,"sourceCodeEnd":191,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/plugins/PassboltEe/AccountRecovery/src/Service/AccountRecoveryRequests/AccountRecoveryRequestCreateService.php#L155-L191","documentation":"BadRequestException from getAndAssertToken when the supplied recover token either does not exist, does not belong to the user, is of the wrong type, is inactive, or has expired. AuthenticationTokenGetService::getActiveNotExpiredOrFail() raised a NotFoundException which is translated into this message.","triggerScenarios":"POST /account-recovery/requests with an authentication_token.token that is not found via getActiveNotExpiredOrFail($token, $userId, TYPE_RECOVER): wrong id, already consumed/deactivated by a previous recovery request, expired, or belonging to another user.","commonSituations":"Reusing a token from an earlier recovery attempt (tokens are deactivated after use); user waited past token expiry; copy/paste truncation of the token; environment mismatch (token created on another instance/database).","solutions":["Restart the account recovery flow to generate a fresh token and retry immediately","Verify the token matches the same user_id in the request","Check the authentication_tokens table: row must have type 'recover', active=true, and created within expiry window"],"exampleFix":"// before\noldToken = <token from a completed recovery attempt> // inactive -> 400\n// after\nstart a new recovery flow, use the newly issued token in the create call","handlingStrategy":"retry","validationCode":"// Pre-check token state client-side: it must be recent, type 'recover', and not previously used","typeGuard":"null","tryCatchPattern":"try { $service->create($data); } catch (BadRequestException $e) { if (str_contains($e->getMessage(), 'not valid or has expired')) { restartRecoveryFlow(); } }","preventionTips":["Never reuse a token from a prior recovery attempt (they are deactivated on use)","Use the token promptly; tokens expire","Ensure client and server point to the same passbolt instance/database","Catch this error and automatically restart the recovery flow"],"tags":["authentication","token-expired","account-recovery","bad-request"],"backgroundTag":"jwt-token-expired","analyzedSha":"31c1bbc10f32808a607fa9bd81891e898779c0bc","analyzedAt":"2026-09-17T00:04:38.960Z","contentChangedAt":"2026-09-17T00:04:38.960Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}