passbolt/passbolt_api · error · BadRequestException
JWT token is missing.
Error message
JWT token is missing.
What it means
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.
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).
Example fix
// before new GoogleIdToken($provider, ['access_token' => $token]); // id_token missing // after new GoogleIdToken($provider, ['id_token' => $tokenResponse['id_token']]);
Defensive patterns
Strategy: validation
Validate before calling
$idToken = $tokenResponse['id_token'] ?? null;
if (!is_string($idToken) || $idToken === '') {
throw new RuntimeException('No id_token in provider response; check openid scope');
} Type guard
function hasIdToken(array $options): bool {
return isset($options['id_token']) && is_string($options['id_token']) && $options['id_token'] !== '';
} Try / catch
try {
$token = new GoogleIdToken($provider, ['id_token' => $idToken]);
} catch (BadRequestException $e) {
// id_token missing: ensure openid scope was requested and correct field extracted
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- No claims
- The aud (client id) parameter is invalid.
- The email claim is not found or invalid.
- The iss (issuer) parameter does not match.
- The iss (issuer) parameter is invalid.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/eb9ba8007e896229.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/PassboltEe/Sso/src/Utility/OpenId/BaseIdToken.php:62
/**
* @var array
*/
protected array $idTokenClaims;
/**
* @param array $options such as access_token, refresh_token and id_token
* @param \Passbolt\Sso\Utility\Provider\AbstractOauth2Provider $provider provider
* @throws \Cake\Http\Exception\InternalErrorException if keys to verify JWT cannot be fetched or validated
* @throws \Cake\Http\Exception\BadRequestException if JWT doesn't validate
*/
public function __construct(array $options, AbstractOauth2Provider $provider)
{
parent::__construct($options);
$this->provider = $provider;
if (empty($options['id_token']) || !is_string($options['id_token'])) {
throw new BadRequestException(__('JWT token is missing.'));
}
$this->idToken = $options['id_token'];
unset($this->values['id_token']);
$keys = $provider->getJwtVerificationKeys();
try {
/**
* To fix "Firebase\JWT\BeforeValidException: Cannot handle token prior" error.
*
* @link https://github.com/googleapis/google-api-php-client/issues/1630
* @link https://stackoverflow.com/questions/53658600/uncaught-exception-firebase-jwt-beforevalidexception-with-message-cannot-hand
*/
JWT::$leeway = Configure::read('passbolt.plugins.sso.security.jwtLeeway');
$tokenClaims = (array)JWT::decode($this->idToken, $keys);
} catch (Exception $exception) {
if (Configure::read('passbolt.plugins.sso.debugEnabled')) {
Log::error('idToken => ' . json_encode($this->idToken));View on GitHub (pinned to 31c1bbc10f)