passbolt/passbolt_api · error · BadRequestException

The email claim is not found or invalid.

Error message

The email claim is not found or invalid.

What it means

assertEmailClaim validates that the token claims contain a valid email. It reads the claim key from `passbolt.plugins.sso.security.oauth2.emailClaimAlias` (default 'email') and checks presence plus EmailValidationRule::check; missing or malformed emails throw BadRequestException('The email claim is not found or invalid.').

Solutions

  1. Ensure the OAuth authorization request includes the `email` (and `profile`) scope so the provider emits the email claim.
  2. Check `passbolt.plugins.sso.security.oauth2.emailClaimAlias` matches the actual claim name your provider returns (inspect the decoded token payload).
  3. In the provider admin console, verify the user account has a primary, verified email address.
  4. Inspect the raw token (debugEnabled or jwt.io) to confirm which claims are actually present.

Example fix

// before
'security' => ['oauth2' => ['emailClaimAlias' => 'user_email']] // provider sends 'email'
// after
'security' => ['oauth2' => ['emailClaimAlias' => 'email']]
Defensive patterns

Strategy: validation

Validate before calling

$alias = Configure::read('passbolt.plugins.sso.security.oauth2.emailClaimAlias') ?? 'email';
if (!isset($claims[$alias]) || !filter_var($claims[$alias], FILTER_VALIDATE_EMAIL)) {
    // request email scope or fix emailClaimAlias before proceeding
}

Type guard

function hasValidEmailClaim(array $claims, string $alias = 'email'): bool {
    return isset($claims[$alias]) && is_string($claims[$alias]) && filter_var($claims[$alias], FILTER_VALIDATE_EMAIL) !== false;
}

Try / catch

try {
    $token->assertTokenClaims($claims);
} catch (BadRequestException $e) {
    if (str_contains($e->getMessage(), 'email claim')) { /* check scopes/claim alias */ }
}

Prevention

When it happens

Trigger: assertTokenClaims runs assertEmailClaim and the configured email claim key is absent from the decoded id_token claims, or its value fails RFC email validation (missing, null, non-string, or syntactically invalid).

Common situations: Provider scope not requesting email (missing `email` scope); Azure/Google tokens lacking email when the user has no verified address; admins renaming the claim via emailClaimAlias but the provider uses a different key (e.g. `mail`, `upn`).

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17). Data as JSON: /api/errors/e810500b0309b63a. Report an issue: GitHub.

Appendix: source

Thrown at plugins/PassboltEe/Sso/src/Utility/OpenId/BaseIdToken.php:130

        $this->assertAudClaim($tokenClaims);
        $this->assertIssClaim($tokenClaims);
        $this->assertEmailClaim($tokenClaims);
    }

    /**
     * Validation email claim against application email validation rule
     *
     * @param array $tokenClaims claims
     * @return void
     * @throws \Cake\Http\Exception\BadRequestException if the claim does not validate
     */
    public function assertEmailClaim(array $tokenClaims): void
    {
        $emailClaim = Configure::read('passbolt.plugins.sso.security.oauth2.emailClaimAlias') ?? 'email';

        if (!isset($tokenClaims[$emailClaim]) || !EmailValidationRule::check($tokenClaims[$emailClaim])) {
            throw new BadRequestException('The email claim is not found or invalid.');
        }
    }

    /**
     * Validate issuer against provider base uri
     * Allows for trailing slash variations
     *
     * @param array $tokenClaims claims
     * @return void
     * @throws \Cake\Http\Exception\BadRequestException if the claim does not validate
     */
    public function assertIssClaim(array $tokenClaims): void
    {
        if (!isset($tokenClaims['iss']) || !is_string($tokenClaims['iss'])) {
            throw new BadRequestException('The iss (issuer) parameter is invalid.');
        }

        $openIdBaseUri = rtrim($this->provider->getOpenIdBaseUri(), '/');

View on GitHub (pinned to 31c1bbc10f)