passbolt/passbolt_api · error · BadRequestException

The iss (issuer) parameter does not match.

Error message

The iss (issuer) parameter does not match.

What it means

After confirming iss is a string, assertIssClaim compares it (trailing-slash-insensitive) with the provider's configured OpenID base URI via $this->provider->getOpenIdBaseUri(). Any mismatch throws BadRequestException('The iss (issuer) parameter does not match.') because the token was issued by a different party than configured — a potential token-confusion attack or a configuration error.

Solutions

  1. Log both values (token iss vs configured base URI) and set `openIdBaseUri`/provider settings to exactly the token's iss value (issuer URI, not the auth endpoint).
  2. Use the provider's discovery document (`/.well-known/openid-configuration`) `issuer` field as the configured value.
  3. For Azure AD, ensure the tenant ID in the issuer matches the configured tenant (commondir vs single-tenant mismatches).
  4. Check for scheme/port/path differences (http vs https, trailing paths) between environments (staging vs production).

Example fix

// before
'openIdBaseUri' => 'https://accounts.google.com/o/oauth2/v2/auth' // auth endpoint
// after
'openIdBaseUri' => 'https://accounts.google.com' // matches token iss
Defensive patterns

Strategy: validation

Validate before calling

$expected = rtrim($openIdConfiguration['issuer'], '/');
$actual = rtrim($claims['iss'] ?? '', '/');
if ($actual !== $expected) {
    throw new RuntimeException("Issuer mismatch: token=$actual configured=$expected");
}

Type guard

function issuerMatches(array $claims, string $configuredBaseUri): bool {
    return is_string($claims['iss'] ?? null)
        && rtrim($claims['iss'], '/') === rtrim($configuredBaseUri, '/');
}

Try / catch

try {
    $token->assertTokenClaims($claims);
} catch (BadRequestException $e) {
    if (str_contains($e->getMessage(), 'does not match')) {
        // log both iss values; align openIdBaseUri with discovery 'issuer'
    }
}

Prevention

When it happens

Trigger: Decoded token's `iss` differs from rtrim()'d getOpenIdBaseUri(): e.g. iss is `https://accounts.google.com` but configured base URI is `https://accounts.google.com/o/oauth2/v2/auth`, or tenant-specific issuer URLs (Azure: https://login.microsoftonline.com/{tenant}/v2.0) that don't match the configured value.

Common situations: Copying the authorization/token endpoint into the issuer/base-URI setting instead of the discovery issuer; Azure tenant changed; switching between http/https or adding/removing paths; provider migrating issuer URLs.

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/c3b8b55d4978b244. Report an issue: GitHub.

Appendix: source

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

    /**
     * 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(), '/');
        $iss = rtrim($tokenClaims['iss'], '/');
        if ($iss !== $openIdBaseUri) {
            throw new BadRequestException('The iss (issuer) parameter does not match.');
        }
    }

    /**
     * https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.3
     *
     * @param array $tokenClaims claims
     * @return void
     * @throws \Cake\Http\Exception\BadRequestException if the claim does not validate
     */
    public function assertAudClaim(array $tokenClaims): void
    {
        if (isset($tokenClaims['aud'])) {
            if (is_string($tokenClaims['aud'])) {
                $auds[] = $tokenClaims['aud'];
            } else {
                $auds = $tokenClaims['aud'];
            }

View on GitHub (pinned to 31c1bbc10f)