Error message
jwt issuer invalid. expected: https://login.example.com/
The text after "expected:" is whatever you configured; with an array of issuers the values are joined with commas.
jsonwebtoken (Node.js) — JsonWebTokenError from jwt.verify()
What it means
The token's `iss` claim is not equal to the `issuer` option you passed to `jwt.verify()` (or the token has no `iss` at all). The text after "expected:" is your configured value, not the token's. jsonwebtoken compares strings exactly, so the signature is valid but the token claims to come from somewhere your service does not trust.
Why it happens
Same issuer, different spelling
commonA trailing slash, http vs https, upper-case host, or a missing path segment (`/realms/main`, `/v2.0`, `/oauth2/default`). Identity providers write `iss` exactly as their discovery document's `issuer`; the verifier must use the same string.
The token has no iss claim
occasionalInternal token services often omit `iss`. With the `issuer` option set, a missing claim fails with the same message, because there is nothing to match.
The token really comes from another issuer
occasionalA token from a different tenant, environment or provider. Here the error is the protection working: keep rejecting it.
How to fix it
- 1.
Print the token's iss as JSON
`JSON.stringify` makes invisible differences visible — trailing slashes, whitespace, a different path. Then copy the provider's `issuer` from `/.well-known/openid-configuration` into your configuration.
js const { iss } = jwt.decode(token) ?? {}; console.log(JSON.stringify(iss)); // compare byte for byte with your issuer option - 2.
Accept several issuers only deliberately
The `issuer` option accepts an array. Use it for a planned migration between two spellings or issuers you control — and pair each issuer with its own keys.
js jwt.verify(token, publicKey, { algorithms: ["ES256"], issuer: ["https://login.example.com/", "https://login.example.com"], // during a migration only });