{"record":{"id":"b399e884e1287bc1","repo":"passbolt/passbolt_api","slug":"accesstoken-should-be-an-instance-of-baseidtoken-class","errorCode":null,"errorMessage":"AccessToken should be an instance of BaseIdToken class.","messagePattern":"AccessToken should be an instance of BaseIdToken class\\.","errorType":"exception","errorClass":"InternalErrorException","httpStatus":500,"severity":"error","filePath":"plugins/PassboltEe/Sso/src/Utility/Provider/AbstractOauth2Provider.php","lineNumber":262,"sourceCode":"    protected function createAccessToken(array $response, AbstractGrant $grant): AccessToken\n    {\n        return new BaseIdToken($response, $this);\n    }\n\n    /**\n     * @inheritDoc\n     */\n    public function getResourceOwner(AccessToken $token): ResourceOwnerInterface\n    {\n        // We get resource owner information from id_token only\n        // We could fall back calling user info API user access_token but we rather not\n        if ($token instanceof BaseIdToken) {\n            $data = $token->getIdTokenClaims();\n            // e.g. token is passed to match League\\AbstractProvider interface but not used\n            return $this->createResourceOwner($data, $token);\n        }\n\n        throw new InternalErrorException('AccessToken should be an instance of BaseIdToken class.');\n    }\n\n    /**\n     * Get JWT verification keys from Google.\n     *\n     * @return array\n     */\n    public function getJwtVerificationKeys(): array\n    {\n        $openIdConfiguration = $this->getOpenIdConfiguration();\n        $keysUri = $openIdConfiguration['jwks_uri'];\n\n        $factory = $this->getRequestFactory();\n        $request = $factory->getRequestWithOptions('get', $keysUri, []);\n\n        try {\n            $response = $this->getParsedResponse($request);\n        } catch (Throwable $exception) {","sourceCodeStart":244,"sourceCodeEnd":280,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/plugins/PassboltEe/Sso/src/Utility/Provider/AbstractOauth2Provider.php#L244-L280","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\n$token = $provider->getAccessToken('authorization_code', ['code' => $code]); // plain League AccessToken\n// after\n$token = $provider->getIdToken('authorization_code', ['code' => $code]); // BaseIdToken subclass\n$resourceOwner = $provider->getResourceOwner($token);","handlingStrategy":"type-guard","validationCode":"if (!$token instanceof \\Passbolt\\Sso\\Utility\\BaseIdToken) {\n    throw new \\InvalidArgumentException('Expected BaseIdToken, got ' . get_class($token));\n}","typeGuard":"function isBaseIdToken($token): bool {\n    return $token instanceof \\Passbolt\\Sso\\Utility\\BaseIdToken;\n}","tryCatchPattern":"try {\n    $resourceOwner = $provider->getResourceOwner($token);\n} catch (\\Cake\\Http\\Exception\\InternalErrorException $e) {\n    $this->log('SSO token type mismatch: ' . $e->getMessage());\n    throw new SsoAuthenticationException('Invalid id_token returned by provider.');\n}","preventionTips":["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"],"tags":["oauth2","sso","internal-error","type-mismatch"],"backgroundTag":"type-mismatch","analyzedSha":"31c1bbc10f32808a607fa9bd81891e898779c0bc","analyzedAt":"2026-09-17T00:04:38.960Z","contentChangedAt":"2026-09-17T00:04:38.960Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}