{"record":{"id":"c3b8b55d4978b244","repo":"passbolt/passbolt_api","slug":"the-iss-issuer-parameter-does-not-match","errorCode":null,"errorMessage":"The iss (issuer) parameter does not match.","messagePattern":"The iss \\(issuer\\) parameter does not match\\.","errorType":"validation","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"plugins/PassboltEe/Sso/src/Utility/OpenId/BaseIdToken.php","lineNumber":151,"sourceCode":"\n    /**\n     * Validate issuer against provider base uri\n     * Allows for trailing slash variations\n     *\n     * @param array $tokenClaims claims\n     * @return void\n     * @throws \\Cake\\Http\\Exception\\BadRequestException if the claim does not validate\n     */\n    public function assertIssClaim(array $tokenClaims): void\n    {\n        if (!isset($tokenClaims['iss']) || !is_string($tokenClaims['iss'])) {\n            throw new BadRequestException('The iss (issuer) parameter is invalid.');\n        }\n\n        $openIdBaseUri = rtrim($this->provider->getOpenIdBaseUri(), '/');\n        $iss = rtrim($tokenClaims['iss'], '/');\n        if ($iss !== $openIdBaseUri) {\n            throw new BadRequestException('The iss (issuer) parameter does not match.');\n        }\n    }\n\n    /**\n     * https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.3\n     *\n     * @param array $tokenClaims claims\n     * @return void\n     * @throws \\Cake\\Http\\Exception\\BadRequestException if the claim does not validate\n     */\n    public function assertAudClaim(array $tokenClaims): void\n    {\n        if (isset($tokenClaims['aud'])) {\n            if (is_string($tokenClaims['aud'])) {\n                $auds[] = $tokenClaims['aud'];\n            } else {\n                $auds = $tokenClaims['aud'];\n            }","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/plugins/PassboltEe/Sso/src/Utility/OpenId/BaseIdToken.php#L133-L169","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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).","Use the provider's discovery document (`/.well-known/openid-configuration`) `issuer` field as the configured value.","For Azure AD, ensure the tenant ID in the issuer matches the configured tenant (commondir vs single-tenant mismatches).","Check for scheme/port/path differences (http vs https, trailing paths) between environments (staging vs production)."],"exampleFix":"// before\n'openIdBaseUri' => 'https://accounts.google.com/o/oauth2/v2/auth' // auth endpoint\n// after\n'openIdBaseUri' => 'https://accounts.google.com' // matches token iss","handlingStrategy":"validation","validationCode":"$expected = rtrim($openIdConfiguration['issuer'], '/');\n$actual = rtrim($claims['iss'] ?? '', '/');\nif ($actual !== $expected) {\n    throw new RuntimeException(\"Issuer mismatch: token=$actual configured=$expected\");\n}","typeGuard":"function issuerMatches(array $claims, string $configuredBaseUri): bool {\n    return is_string($claims['iss'] ?? null)\n        && rtrim($claims['iss'], '/') === rtrim($configuredBaseUri, '/');\n}","tryCatchPattern":"try {\n    $token->assertTokenClaims($claims);\n} catch (BadRequestException $e) {\n    if (str_contains($e->getMessage(), 'does not match')) {\n        // log both iss values; align openIdBaseUri with discovery 'issuer'\n    }\n}","preventionTips":["Configure openIdBaseUri from the discovery document's `issuer` field, never from auth/token endpoint URLs","Diff token iss vs configured URI when promoting config between environments","For Azure, pin the correct tenant issuer URL for your app registration"],"tags":["jwt","issuer","claims","openid","sso"],"backgroundTag":"schema-validation-failed","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"}