{"record":{"id":"c28179ed54fb04e8","repo":"thephpleague/oauth2-server","slug":"access-denied","errorCode":"access_denied","errorMessage":"The user denied the request","messagePattern":"The user denied the request","errorType":"http","errorClass":"OAuthServerException","httpStatus":401,"severity":"warning","filePath":"src/Grant/AuthCodeGrant.php","lineNumber":399,"sourceCode":"                throw new LogicException('An error was encountered when JSON encoding the authorization request response');\n            }\n\n            $response = new RedirectResponse();\n            $response->setRedirectUri(\n                $this->makeRedirectUri(\n                    $finalRedirectUri,\n                    [\n                        'code'  => $this->encrypt($jsonPayload),\n                        'state' => $authorizationRequest->getState(),\n                    ]\n                )\n            );\n\n            return $response;\n        }\n\n        // The user denied the client, redirect them back with an error\n        throw OAuthServerException::accessDenied(\n            'The user denied the request',\n            $this->makeRedirectUri(\n                $finalRedirectUri,\n                [\n                    'state' => $authorizationRequest->getState(),\n                ]\n            )\n        );\n    }\n}\n","sourceCodeStart":381,"sourceCodeEnd":410,"githubUrl":"https://github.com/thephpleague/oauth2-server/blob/9d2f6fc0a0b5aa1bb02506971d3a4ecff2c6526c/src/Grant/AuthCodeGrant.php#L381-L410","documentation":"access_denied: the user (resource owner) denied the client's authorization request. completeAuthorizationRequest() detects the request was not approved and throws, redirecting back to the client with error=access_denied and the original state parameter.","triggerScenarios":"Authorization request completes with AuthorizationRequest::setAuthorizationApproved(false) — typically the user clicked 'Deny'/'Cancel' on your consent screen and you called completeAuthorizationRequest() anyway.","commonSituations":"Users declining consent — normal flow, not a bug; consent UI not distinguishing approve/deny before calling the server; automated tests exercising the deny path; users abandoning a consent dialog that still submits a denial.","solutions":["Treat this as an expected outcome: catch OAuthServerException and check getCode() === 'access_denied' to show 'you denied access' in the client app","In your authorization-confirm controller, only call completeAuthorizationRequest() after setAuthorizationApproved(true); return the redirect response directly for denials","Inspect the exception's redirect payload to recover state and inform the user","Retry the flow only if the user explicitly wants to try again"],"exampleFix":"// before\ntry {\n    $response = $server->completeAuthorizationRequest($authRequest, $response);\n} catch (OAuthServerException $e) { throw $e; }\n// after\ntry {\n    $response = $server->completeAuthorizationRequest($authRequest, $response);\n} catch (OAuthServerException $e) {\n    if ($e->getCode() === 9 /* access_denied */) {\n        return new Response(['message' => 'Authorization was denied by the user']);\n    }\n    throw $e;\n}","handlingStrategy":"try-catch","validationCode":"// before calling completeAuthorizationRequest, branch explicitly\nif (!$authorizationRequest->isAuthorizationApproved()) {\n    return new Response(['message' => 'User denied access']); // do not call the server\n}","typeGuard":"function wasApproved($authorizationRequest): bool {\n    return $authorizationRequest !== null && $authorizationRequest->isAuthorizationApproved() === true;\n}","tryCatchPattern":"try {\n    $response = $server->completeAuthorizationRequest($authRequest, $response);\n} catch (OAuthServerException $e) {\n    if ($e->getErrorType() === 'access_denied') {\n        return new Response(['message' => 'Authorization was denied by the user']);\n    }\n    throw $e;\n}","preventionTips":["Only mark the request approved when the user explicitly consents","Handle access_denied as a normal UX outcome, not a crash","Preserve and echo the state parameter for CSRF continuity","Show a clear deny path in the consent UI so denials are intentional"],"tags":["oauth2","access-denied","user-consent","authorization-endpoint"],"backgroundTag":"permission-denied","analyzedSha":"9d2f6fc0a0b5aa1bb02506971d3a4ecff2c6526c","analyzedAt":"2026-09-15T22:33:30.452Z","contentChangedAt":"2026-09-15T22:33:30.452Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}