{"record":{"id":"fbb8480be37adcf8","repo":"passbolt/passbolt_api","slug":"the-authentication-token-is-not-valid-recoverabortservice","errorCode":null,"errorMessage":"The authentication token is not valid.","messagePattern":"The authentication token is not valid\\.","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"src/Service/Setup/RecoverAbortService.php","lineNumber":83,"sourceCode":"\n    /**\n     * Return the token or fail\n     *\n     * @param string $token token.token\n     * @param string $userId User ID\n     * @return void\n     * @throws \\Cake\\Http\\Exception\\BadRequestException if the token is missing, expired, invalid, or already consumed\n     */\n    protected function assertAndConsumeToken(string $token, string $userId): void\n    {\n        try {\n            $tokenEntity = (new AuthenticationTokenGetService())\n                ->getActiveNotExpiredOrFail($token, $userId, AuthenticationToken::TYPE_RECOVER);\n        } catch (NotFoundException $exception) {\n            if (Configure::read('debug')) {\n                Log::error('getActiveNotExpiredOrFail() failed: ' . $exception->getMessage());\n            }\n            throw new BadRequestException(__('The authentication token is not valid.'));\n        }\n\n        /** @var \\App\\Model\\Table\\AuthenticationTokensTable $authenticationTokensTable */\n        $authenticationTokensTable = $this->fetchTable('AuthenticationTokens');\n        if (!$authenticationTokensTable->setInactive($tokenEntity->token)) {\n            // Lost the concurrent-consume race.\n            throw new BadRequestException(__('The authentication token is not valid.'));\n        }\n    }\n\n    /**\n     * Return the user for matching the requesting id\n     *\n     * @param string $userId the user uuid\n     * @throws \\Cake\\Http\\Exception\\BadRequestException if the user id is not a valid uuid\n     * @throws \\Cake\\Http\\Exception\\BadRequestException if the user was deleted or has not completed the setup\n     * @return \\App\\Model\\Entity\\User\n     */","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/src/Service/Setup/RecoverAbortService.php#L65-L101","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const t = await getTokenRow(tokenId);\nconst valid = t && t.type === 'recover' && t.active && new Date(t.expired) > new Date() && t.user_id === userId;","typeGuard":null,"tryCatchPattern":"try { await recoverAbort(userId, tokenId); }\ncatch (e) { if (isInvalidToken(e)) startNewRecoveryFlow(userId); else throw e; }","preventionTips":["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"],"tags":["recover","authentication-token","token-expired"],"backgroundTag":"invalid-identifier","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"}