passbolt/passbolt_api · error · InternalErrorException
AccessToken should be an instance of BaseIdToken class.
Error message
AccessToken should be an instance of BaseIdToken class.
What it means
AbstractOauth2Provider::getResourceOwner() is an adapter over the League OAuth2 provider interface, which accepts any AccessTokenInterface. Passbolt's SSO only works with its own BaseIdToken subclass carrying id_token claims, so when a plain League AccessToken is passed it cannot build the resource owner and throws an InternalErrorException.
Solutions
- Ensure the provider's token exchange returns a BaseIdToken instance (implement/keep getTokenCredentials() so it wraps the id_token into BaseIdToken)
- Check that the subclass of AbstractOauth2Provider does not override getTokenCredentials to return a plain League AccessToken
- Verify the id_token (JWT) is actually present in the provider's response; a missing id_token commonly makes the code fall back to a generic token
- Reproduce with a unit test passing a League AccessToken to getResourceOwner() to confirm which call site supplies it
Example fix
// before
$token = $provider->getAccessToken('authorization_code', ['code' => $code]); // plain League AccessToken
// after
$token = $provider->getIdToken('authorization_code', ['code' => $code]); // BaseIdToken subclass
$resourceOwner = $provider->getResourceOwner($token); Defensive patterns
Strategy: type-guard
Validate before calling
if (!$token instanceof \Passbolt\Sso\Utility\BaseIdToken) {
throw new \InvalidArgumentException('Expected BaseIdToken, got ' . get_class($token));
} Type guard
function isBaseIdToken($token): bool {
return $token instanceof \Passbolt\Sso\Utility\BaseIdToken;
} Try / catch
try {
$resourceOwner = $provider->getResourceOwner($token);
} catch (\Cake\Http\Exception\InternalErrorException $e) {
$this->log('SSO token type mismatch: ' . $e->getMessage());
throw new SsoAuthenticationException('Invalid id_token returned by provider.');
} Prevention
- Always obtain the token through the SSO plugin's getIdToken flow, never the base League getAccessToken
- Add instanceof assertions at token hand-off points in custom providers
- Unit test custom provider subclasses with a plain AccessToken to catch regressions
- Keep League/oauth2-client versions aligned with the SSO plugin's requirements
When it happens
Trigger: Calling getResourceOwner() (directly or via the OAuth2 token-exchange flow) with a token object that is an AccessTokenInterface but not a Passbolt BaseIdToken instance — e.g. a token obtained through the standard League token exchange instead of the SSO-specific code path, or a misconfigured provider returning a generic token class.
Common situations: Custom or overridden OAuth2 provider code constructing the wrong token class; upgrading League/oauth2-client so the base getTokenCredentials() path yields an AccessToken instead of BaseIdToken; subclass overriding createAccessToken/getAccessToken and dropping the id_token handling.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Invalid provider data. Expected OAuth2 settings.
- Cannot parse JWKS endpoint response.
- Could not delete the draft SSO settings.
- Could not delete the SSO settings.
- Could not save the SSO state, please try again later.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/b399e884e1287bc1.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/PassboltEe/Sso/src/Utility/Provider/AbstractOauth2Provider.php:262
protected function createAccessToken(array $response, AbstractGrant $grant): AccessToken
{
return new BaseIdToken($response, $this);
}
/**
* @inheritDoc
*/
public function getResourceOwner(AccessToken $token): ResourceOwnerInterface
{
// We get resource owner information from id_token only
// We could fall back calling user info API user access_token but we rather not
if ($token instanceof BaseIdToken) {
$data = $token->getIdTokenClaims();
// e.g. token is passed to match League\AbstractProvider interface but not used
return $this->createResourceOwner($data, $token);
}
throw new InternalErrorException('AccessToken should be an instance of BaseIdToken class.');
}
/**
* Get JWT verification keys from Google.
*
* @return array
*/
public function getJwtVerificationKeys(): array
{
$openIdConfiguration = $this->getOpenIdConfiguration();
$keysUri = $openIdConfiguration['jwks_uri'];
$factory = $this->getRequestFactory();
$request = $factory->getRequestWithOptions('get', $keysUri, []);
try {
$response = $this->getParsedResponse($request);
} catch (Throwable $exception) {View on GitHub (pinned to 31c1bbc10f)