{"record":{"id":"a5d5455ced2049ae","repo":"passbolt/passbolt_api","slug":"only-guests-are-allowed-to-proceed-with-account-recovery","errorCode":null,"errorMessage":"Only guests are allowed to proceed with account recovery.","messagePattern":"Only guests are allowed to proceed with account recovery\\.","errorType":"http","errorClass":"ForbiddenException","httpStatus":403,"severity":"error","filePath":"plugins/PassboltEe/AccountRecovery/src/Controller/AccountRecoveryContinue/AccountRecoveryContinueController.php","lineNumber":72,"sourceCode":"     * @return void\n     * @throws \\Cake\\Http\\Exception\\BadRequestException if the data provided is not valid\n     */\n    public function get(\n        ?string $userId,\n        ?string $tokenId,\n        AccountRecoveryContinueAggregatorService $accountRecoveryContinueService\n    ): void {\n        if (!isset($userId) || !Validation::uuid($userId)) {\n            throw new BadRequestException(__('The user id is invalid.'));\n        }\n        if (!isset($tokenId) || !Validation::uuid($tokenId)) {\n            throw new BadRequestException(__('The authentication token id is invalid.'));\n        }\n\n        if ($this->getRequest()->is('json')) {\n            // Do not allow logged in user to recover\n            if ($this->User->role() !== Role::GUEST) {\n                throw new ForbiddenException(__('Only guests are allowed to proceed with account recovery.'));\n            }\n\n            (new AccountRecoveryRequestGetService())->getOrFail($userId, $tokenId);\n\n            $data = $accountRecoveryContinueService->get();\n\n            $this->success(__('The operation was successful.'), $data);\n        } else {\n            $this->renderHtml();\n        }\n    }\n\n    /**\n     * @return void\n     */\n    protected function renderHtml(): void\n    {\n        $this->viewBuilder()","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/plugins/PassboltEe/AccountRecovery/src/Controller/AccountRecoveryContinue/AccountRecoveryContinueController.php#L54-L90","documentation":"ForbiddenException thrown by AccountRecoveryContinueController::get() when a non-guest (authenticated) user attempts to proceed with account recovery. Only anonymous (GUEST role) users may start the recovery flow.","triggerScenarios":"A logged-in user (admin or user role) opens an account recovery continue link in the same browser session; SSO/cookie auto-login occurring before the recovery flow completes.","commonSituations":"User clicks a recovery link while already logged in; session cookie from another account persisting; testing recovery while authenticated.","solutions":["Log out (and clear the session cookie) before using the recovery link","Open the recovery link in a private/incognito window or a different browser profile","Verify SSO settings aren't silently re-authenticating the user","For tests, ensure the fixture sets no authenticated session"],"exampleFix":"// before\n$this->loginAs('ada@passbolt.test');\n$this->get('/account-recovery/continue/...'); // 403\n// after\n$this->logout();\n$this->get('/account-recovery/continue/...'); // 200","handlingStrategy":"try-catch","validationCode":"const role = await selfClient.getOwnRole();\nif (role !== 'guest') {\n  throw new Error('log out before starting account recovery');\n}","typeGuard":null,"tryCatchPattern":"try {\n  await accountRecoveryContinueService.get(userId, tokenId);\n} catch (ApiError e) {\n  if (e.status === 403 && e.message.includes('Only guests')) {\n    await logout();\n    retryRecoveryFlow();\n  }\n}","preventionTips":["Clear session cookies before using recovery links","Open recovery links in incognito/private windows when already logged in","Check SSO auto-login settings that may re-authenticate users","In tests, avoid attaching authenticated sessions to guest-only endpoints"],"tags":["php","cakephp","authorization","forbidden"],"backgroundTag":"permission-denied","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"}