keycloak/keycloak · error · VerificationException
Invalid token issuer. Expected '...'
Error message
Invalid token issuer. Expected '...'
What it means
Thrown by RealmUrlCheck.test when the token's issuer ('iss' claim) does not equal the configured realmUrl. The check performs an exact string equality, so any trailing slash, scheme difference, realm-name casing, or host alias mismatch causes rejection. The expected value is interpolated into the message for diagnosis.
Source
Thrown at core/src/main/java/org/keycloak/TokenVerifier.java:114
public static class RealmUrlCheck implements Predicate<JsonWebToken> {
private static final RealmUrlCheck NULL_INSTANCE = new RealmUrlCheck(null);
private final String realmUrl;
public RealmUrlCheck(String realmUrl) {
this.realmUrl = realmUrl;
}
@Override
public boolean test(JsonWebToken t) throws VerificationException {
if (this.realmUrl == null) {
throw new VerificationException("Realm URL not set");
}
if (! this.realmUrl.equals(t.getIssuer())) {
throw new VerificationException("Invalid token issuer. Expected '" + this.realmUrl + "'");
}
return true;
}
}
public static class TokenTypeCheck implements Predicate<JsonWebToken> {
private static final TokenTypeCheck INSTANCE_DEFAULT_TOKEN_TYPE = new TokenTypeCheck(Arrays.asList(TokenUtil.TOKEN_TYPE_BEARER));
private final List<String> tokenTypes;
public TokenTypeCheck(List<String> tokenTypes) {
this.tokenTypes = tokenTypes;
}
@Override
public boolean test(JsonWebToken t) throws VerificationException {View on GitHub (pinned to 66c7e15a37)
Solutions
- Print the token's 'iss' claim and the configured realmUrl side by side; align them exactly.
- Ensure realmUrl includes the full /realms/{realm} suffix, e.g. https://host/realms/myrealm.
- If behind a proxy, set the Keycloak hostname/issuer settings (KC_HOSTNAME_URL / hostname.issuer) so the 'iss' claim matches what verifiers expect.
- Normalize trailing slashes and scheme (http/https) on both sides.
Example fix
// before: missing /realms/{realm} suffix
verifier.realmUrl("https://keycloak.example.com");
// after: full realm issuer URL
verifier.realmUrl("https://keycloak.example.com/realms/myrealm"); Defensive patterns
Strategy: validation
Validate before calling
// Normalize and compare issuers before verification
String expected = normalizeIssuer(config.get("realmIssuerUrl"));
String actual = parseUnsafe(token).getIssuer();
if (!expected.equals(actual)) {
// reject or correct config before verifying
}
static String normalizeIssuer(String s) { return s == null ? null : s.replaceAll("/+$/, ""); } Type guard
static boolean issuersMatch(String expected, String actual) {
if (expected == null || actual == null) return false;
return expected.replaceAll("/+$/", "").equals(actual.replaceAll("/+$/", ""));
} Try / catch
try {
verifier.realmUrl(realmUrl).verify();
} catch (VerificationException e) {
if (e.getMessage().startsWith("Invalid token issuer")) {
// log expected vs actual, correct config or proxy hostname settings
} else throw e;
} Prevention
- Configure the Keycloak hostname/issuer so the 'iss' claim matches what verifiers expect.
- Normalize trailing slashes and http/https on both sides before comparison.
- Print the token's 'iss' claim during integration tests to catch mismatches early.
When it happens
Trigger: Verifying a token whose 'iss' claim differs in any way from the realmUrl supplied to TokenVerifier.realmUrl(...). Common triggers: realmUrl set to the base Keycloak URL without the /realms/{realm} path, an http vs https mismatch, a trailing-slash difference, or verifying a token from realm A against realm B's URL.
Common situations: Frontend behind a reverse proxy where the issuer claim uses the external URL but the verifier is configured with the internal URL, realm renames, or copying verifier config between environments without updating the issuer.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Realm URL not set
- Expected audience not available in the token
- Expected issuedFor doesn't match
- Subject missing in token
- Token is not active
AI-assisted analysis of keycloak/keycloak@66c7e15a37 (2026-08-14).
Data as JSON: /api/errors/31d32c4008049c94.
Report an issue: GitHub.