passbolt/passbolt_api · error · IdentityProviderException
$response->getReasonPhrase() (dynamic provider error)
Error message
$response->getReasonPhrase() (dynamic provider error)
What it means
When Google's response contains an 'error' key but not in the expected string error/error_description shape, checkResponse() falls back to throwing league/oauth2-client's IdentityProviderException built from the raw HTTP status line, status code and body. This covers malformed or non-standard error payloads (arrays, HTML error pages, empty bodies with error status codes).
Solutions
- Inspect the exception's response body/data to see the raw payload and HTTP status code to identify what actually came back.
- Verify the server can reach accounts.google.com directly (curl) and that no proxy intercepts HTTPS traffic with its own error pages.
- Update the league/oauth2-client / guzzle packages to current versions to ensure consistent response parsing.
- If behind a proxy, add accounts.google.com to the egress allowlist and disable HTTPS interception for it.
- Retry after confirming Google status dashboards show no incident.
Example fix
// before: proxy returns HTML for blocked hosts // data['error'] is set but non-string (parsed garbage) -> IdentityProviderException with raw body // after: allow Google through the egress proxy so real JSON reaches checkResponse() curl -I https://accounts.google.com/.well-known/openid-configuration # expect HTTP 200 and application/json, not a proxy HTML error page
Defensive patterns
Strategy: try-catch
Validate before calling
$decoded = json_decode((string)$response->getBody(), true);
if (json_last_error() !== JSON_ERROR_NONE || !is_array($decoded)) {
// non-JSON body from Google or an intercepting proxy — fail fast with a clear message
} Try / catch
try {
$token = $provider->getAccessToken('jwt_bearer', [...]);
} catch (\League\OAuth2\Client\Provider\Exception\IdentityProviderException $e) {
$this->log('HTTP ' . $e->getCode() . ' from Google: ' . $e->getResponseBody());
// treat as infrastructure/proxy problem, not user error
} Prevention
- Ensure direct egress to accounts.google.com without HTTPS-intercepting proxies
- Pin and update league/oauth2-client and guzzlehttp/guzzle versions
- Check response Content-Type is application/json in integration tests
- Alert on 5xx HTML bodies from the IdP path
When it happens
Trigger: Google (or an intermediary proxy) returns a non-JSON or malformed error body — e.g. an HTML 502/503 page from a load balancer, a JSON error object where 'error' is an array — while data['error'] is non-empty, so the structured GoogleException branch does not apply.
Common situations: Corporate proxies/firewalls intercepting outbound calls to accounts.google.com; Google outages returning HTML error pages; MTU or TLS issues producing truncated responses; mismatched response parsing when response body isn't JSON.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- $data['error'] (dynamic provider error)
- $data['error'] (dynamic provider error)
- response->getReasonPhrase()
- $response->getReasonPhrase() (dynamic provider error)
- Single sign-on failed. Provider error
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/9b2a65e549266a7f.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/PassboltEe/Sso/src/Utility/Google/Provider/GoogleProvider.php:71
*/
protected function createResourceOwner(array $response, AccessToken $token): ResourceOwnerInterface
{
return new GoogleResourceOwner($response);
}
/**
* @inheritDoc
*/
protected function checkResponse(ResponseInterface $response, $data): void
{
if (empty($data['error'])) {
return;
}
if (is_string($data['error']) && isset($data['error_description']) && is_string($data['error_description'])) {
throw new GoogleException($data['error'], $data['error_description']);
} else {
throw new IdentityProviderException(
$response->getReasonPhrase(),
$response->getStatusCode(),
(string)$response->getBody()
);
}
}
}
View on GitHub (pinned to 31c1bbc10f)