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

  1. Inspect the exception's response body/data to see the raw payload and HTTP status code to identify what actually came back.
  2. Verify the server can reach accounts.google.com directly (curl) and that no proxy intercepts HTTPS traffic with its own error pages.
  3. Update the league/oauth2-client / guzzle packages to current versions to ensure consistent response parsing.
  4. If behind a proxy, add accounts.google.com to the egress allowlist and disable HTTPS interception for it.
  5. 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

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


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)