{"record":{"id":"e9ce99214e0117e0","repo":"BookStackApp/BookStack","slug":"data-error-response-getreasonphrase","errorCode":null,"errorMessage":"{$data['error'] ?? $response->getReasonPhrase()}","messagePattern":"\\{\\$data\\['error'\\] \\?\\? \\$response->getReasonPhrase\\(\\)\\}","errorType":"http","errorClass":"IdentityProviderException","httpStatus":null,"severity":"error","filePath":"app/Access/Oidc/OidcOAuthProvider.php","lineNumber":91,"sourceCode":"    }\n\n    /**\n     * Returns the string that should be used to separate scopes when building\n     * the URL for requesting an access token.\n     */\n    protected function getScopeSeparator(): string\n    {\n        return ' ';\n    }\n\n    /**\n     * Checks a provider response for errors.\n     * @throws IdentityProviderException\n     */\n    protected function checkResponse(ResponseInterface $response, $data): void\n    {\n        if ($response->getStatusCode() >= 400 || isset($data['error'])) {\n            throw new IdentityProviderException(\n                $data['error'] ?? $response->getReasonPhrase(),\n                $response->getStatusCode(),\n                (string) $response->getBody()\n            );\n        }\n    }\n\n    /**\n     * Generates a resource owner object from a successful resource owner\n     * details request.\n     */\n    protected function createResourceOwner(array $response, AccessToken $token): ResourceOwnerInterface\n    {\n        return new GenericResourceOwner($response, '');\n    }\n\n    /**\n     * Creates an access token from a response.","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/BookStackApp/BookStack/blob/18f8469a1c72f8cc8497e9372635e6dea5028071/app/Access/Oidc/OidcOAuthProvider.php#L73-L109","documentation":"This error comes from checkResponse in OidcOAuthProvider (BookStack's league/oauth2-client based provider). When the identity provider's token or authorization response has an HTTP status >= 400 or an 'error' key in the decoded body, an IdentityProviderException is thrown carrying either the provider-reported 'error' string or, if absent, the HTTP reason phrase (e.g. 'Internal Server Error'). It is the library's way of surfacing an OAuth/OIDC protocol-level rejection from the remote IdP.","triggerScenarios":"Any OAuth2 exchange through OidcOAuthProvider (getAccessToken, resource owner fetch, etc.) where the remote OIDC provider returns HTTP >= 400, or returns 200 but includes an 'error' field in the JSON body (e.g. invalid_grant, invalid_client, unauthorized_client).","commonSituations":"Wrong client_id/client_secret in BookStack OIDC config causing invalid_client; expired or replayed authorization code causing invalid_grant; misconfigured redirect URI; IdP outage returning a 5xx; clock skew invalidating tokens.","solutions":["Read the IdentityProviderException body (3rd argument) and the provider's logs to see the exact OAuth error code returned.","Verify APP/oidc client_id, client_secret and redirect URI exactly match the IdP application settings.","Re-run the login flow from scratch (fresh authorize request) — invalid_grant errors are often caused by reusing a consumed code or stale session.","Check the IdP status/health and confirm the issuer/token endpoints are reachable over HTTPS.","Compare scopes requested against those allowed by the IdP application."],"exampleFix":"// before: generic failure hard to debug\ntry { $token = $provider->getAccessToken('authorization_code', [...]); }\ncatch (IdentityProviderException $e) { Log::error($e->getMessage()); }\n// after: log full body from provider for the real cause\ntry { $token = $provider->getAccessToken('authorization_code', [...]); }\ncatch (IdentityProviderException $e) {\n    Log::error('OIDC provider error: ' . $e->getMessage() . ' body: ' . $e->getResponseBody());\n}","handlingStrategy":"try-catch","validationCode":"$body = json_decode((string) $response->getBody(), true);\nif (($response->getStatusCode() >= 400 || isset($body['error']))) {\n    Log::warning('OIDC provider will reject this response', ['status' => $response->getStatusCode(), 'body' => $body]);\n}","typeGuard":"function isProviderError(ResponseInterface $response, ?array $data): bool {\n    return $response->getStatusCode() >= 400 || isset($data['error']);\n}","tryCatchPattern":"try {\n    $token = $provider->getAccessToken('authorization_code', ['code' => $code]);\n} catch (IdentityProviderException $e) {\n    Log::error('OIDC token exchange failed', ['msg' => $e->getMessage(), 'body' => (string) $e->getResponseBody()]);\n    return back()->withErrors(['oidc' => 'Authentication with the identity provider failed.']);\n}","preventionTips":["Triple-check client_id, client_secret and redirect URI against the IdP application config.","Never reuse authorization codes; always start a fresh authorize round-trip on failure.","Monitor IdP status and alert on repeated provider errors.","Keep requested scopes within those permitted by the IdP."],"tags":["oauth2","http-error","identity-provider"],"backgroundTag":"oauth-provider-error-response","analyzedSha":"18f8469a1c72f8cc8497e9372635e6dea5028071","analyzedAt":"2026-09-02T19:49:33.068Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}