{"record":{"id":"ad17922be4cb7e2e","repo":"passbolt/passbolt_api","slug":"you-need-to-login-to-access-this-location","errorCode":null,"errorMessage":"You need to login to access this location.","messagePattern":"You need to login to access this location\\.","errorType":"http","errorClass":"Cake\\Http\\Exception\\ForbiddenException","httpStatus":403,"severity":"error","filePath":"plugins/PassboltCe/JwtAuthentication/src/Authenticator/GpgJwtAuthenticator.php","lineNumber":89,"sourceCode":"    /**\n     * @var \\App\\Model\\Entity\\User $user user\n     * @access protected\n     */\n    protected User $user;\n\n    /**\n     * When an unauthenticated user tries to access a protected page this method is called\n     *\n     * @param \\Cake\\Http\\ServerRequest $request interface for accessing request parameters\n     * @param \\Cake\\Http\\Response $response features and functionality for generating HTTP responses\n     * @throws \\Cake\\Http\\Exception\\ForbiddenException\n     * @return void\n     */\n    public function unauthenticated(ServerRequest $request, Response $response): void\n    {\n        // If it's JSON we show an error message\n        if ($request->is('json')) {\n            throw new ForbiddenException(__('You need to login to access this location.'));\n        }\n        // Otherwise we let the controller handle the redirections\n    }\n\n    /**\n     * Authenticate\n     *\n     * @param \\Psr\\Http\\Message\\ServerRequestInterface $request interface for accessing request parameters\n     * @return \\Authentication\\Authenticator\\ResultInterface User|false the user or false if authentication failed\n     */\n    public function authenticate(ServerRequestInterface $request): ResultInterface\n    {\n        /** @var \\Cake\\Http\\ServerRequest $request */\n\n        try {\n            $this->setRequest($request);\n            $this->init();\n            $verifyToken = $this->verifyChallenge();","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/plugins/PassboltCe/JwtAuthentication/src/Authenticator/GpgJwtAuthenticator.php#L71-L107","documentation":"GpgJwtAuthenticator.unauthenticated is invoked when JWT authentication fails or no token was provided. For JSON requests it throws ForbiddenException 'You need to login to access this location.'; non-JSON requests fall through to controller-level redirection. It is the standard passbolt signal that a valid login (JWT token or GPG auth) is required.","triggerScenarios":"Calling a JSON endpoint that requires authentication with: no Authorization header; an expired or invalid JWT; a missing/malformed 'Bearer <token>' prefix; a token signed by a revoked key. The request must have the JSON Accept/header for this exception path.","commonSituations":"Access token expired (default short-lived JWT) and the client didn't refresh it; scripts/CI hitting the API without logging in; clock skew invalidating tokens; users logged out or account disabled.","solutions":["Perform a login (GPG-based /auth/login or JWT verification flow) to obtain fresh access and refresh tokens.","Use the refresh token against /auth/jwt/refresh to get a new access token when the access token expired.","Send the header exactly as 'Authorization: Bearer <access_token>' and ensure Accept: application/json.","Check client/server clock synchronization if tokens seem to expire immediately.","Confirm the user account is active and not disabled."],"exampleFix":"// before: raw request without token\nfetch('/resources.json');\n\n// after: attach token and refresh on 403\nconst res = await fetch('/resources.json', {\n  headers: { Authorization: `Bearer ${accessToken}`, Accept: 'application/json' }\n});\nif (res.status === 403) await refreshAccessToken();","handlingStrategy":"try-catch","validationCode":"const isExpired = (tok) => Date.now() >= JSON.parse(atob(tok.split('.')[1])).exp * 1000;\nif (!accessToken || isExpired(accessToken)) await refreshOrLogin();","typeGuard":"function hasBearer(auth) { return typeof auth === 'string' && auth.startsWith('Bearer ') && auth.length > 7; }","tryCatchPattern":"try { return await api(path); } catch (e) { if (e.message.includes('You need to login')) { await refreshAccessToken(); return api(path); } throw e; }","preventionTips":["Implement automatic token refresh before access-token expiry.","Always send Authorization: Bearer and Accept: application/json headers.","Detect this message client-side and redirect to the login flow.","Keep server and client clocks synchronized (NTP)."],"tags":["authentication","jwt","gpg","forbidden"],"backgroundTag":"authentication-required","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"}