{"record":{"id":"eb9ba8007e896229","repo":"passbolt/passbolt_api","slug":"jwt-token-is-missing","errorCode":null,"errorMessage":"JWT token is missing.","messagePattern":"JWT token is missing\\.","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"plugins/PassboltEe/Sso/src/Utility/OpenId/BaseIdToken.php","lineNumber":62,"sourceCode":"    /**\n     * @var array\n     */\n    protected array $idTokenClaims;\n\n    /**\n     * @param array $options such as access_token, refresh_token and id_token\n     * @param \\Passbolt\\Sso\\Utility\\Provider\\AbstractOauth2Provider $provider provider\n     * @throws \\Cake\\Http\\Exception\\InternalErrorException if keys to verify JWT cannot be fetched or validated\n     * @throws \\Cake\\Http\\Exception\\BadRequestException if JWT doesn't validate\n     */\n    public function __construct(array $options, AbstractOauth2Provider $provider)\n    {\n        parent::__construct($options);\n\n        $this->provider = $provider;\n\n        if (empty($options['id_token']) || !is_string($options['id_token'])) {\n            throw new BadRequestException(__('JWT token is missing.'));\n        }\n        $this->idToken = $options['id_token'];\n        unset($this->values['id_token']);\n\n        $keys = $provider->getJwtVerificationKeys();\n        try {\n            /**\n             * To fix \"Firebase\\JWT\\BeforeValidException: Cannot handle token prior\" error.\n             *\n             * @link https://github.com/googleapis/google-api-php-client/issues/1630\n             * @link https://stackoverflow.com/questions/53658600/uncaught-exception-firebase-jwt-beforevalidexception-with-message-cannot-hand\n             */\n            JWT::$leeway = Configure::read('passbolt.plugins.sso.security.jwtLeeway');\n\n            $tokenClaims = (array)JWT::decode($this->idToken, $keys);\n        } catch (Exception $exception) {\n            if (Configure::read('passbolt.plugins.sso.debugEnabled')) {\n                Log::error('idToken => ' . json_encode($this->idToken));","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/plugins/PassboltEe/Sso/src/Utility/OpenId/BaseIdToken.php#L44-L80","documentation":"BaseIdToken's constructor requires an `id_token` option that is a non-empty string; it throws BadRequestException('JWT token is missing.') otherwise. The id_token is the OIDC JWT returned by the identity provider and is the core input for all subsequent decoding and claim validation.","triggerScenarios":"Instantiating a concrete BaseIdToken subclass (e.g. GoogleIdToken) with options where `id_token` is absent, null, an empty string, or a non-string value — typically when building the token object from an OAuth2 response that lacked id_token.","commonSituations":"Provider not configured to return id_token (missing openid scope); extracting the token from the wrong array key after a provider payload change; passing the access token instead of the id token under the wrong key.","solutions":["Verify the authorization request includes `openid` (and email/profile) scopes so the provider returns an id_token.","Log/inspect the options array passed to the token constructor to confirm the `id_token` key exists and is a string.","Check that the code reads id_token from the correct response field (token response vs userinfo).","Confirm the provider actually issues id_tokens for the configured flow (some setups need explicit response_type/nonce handling)."],"exampleFix":"// before\nnew GoogleIdToken($provider, ['access_token' => $token]); // id_token missing\n// after\nnew GoogleIdToken($provider, ['id_token' => $tokenResponse['id_token']]);","handlingStrategy":"validation","validationCode":"$idToken = $tokenResponse['id_token'] ?? null;\nif (!is_string($idToken) || $idToken === '') {\n    throw new RuntimeException('No id_token in provider response; check openid scope');\n}","typeGuard":"function hasIdToken(array $options): bool {\n    return isset($options['id_token']) && is_string($options['id_token']) && $options['id_token'] !== '';\n}","tryCatchPattern":"try {\n    $token = new GoogleIdToken($provider, ['id_token' => $idToken]);\n} catch (BadRequestException $e) {\n    // id_token missing: ensure openid scope was requested and correct field extracted\n}","preventionTips":["Always request the `openid email profile` scopes in the authorization URL","Extract id_token, not access_token, when constructing the token object","Log the provider's raw token response shape when integrating a new provider"],"tags":["jwt","openid","sso","missing-token"],"backgroundTag":"missing-required-argument","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"}