While reading through the JWT authentication code, I noticed that when you configure issuerUrl, Authorino verifies the token's signature and expiry.., but it never checks that the token's iss claim actually matches the issuer it discovered. I asked about it, and it was confirmed this is intentional: issuer-checking is opt-in today, done via an authorization rule (CEL / pattern-matching / OPA), and kept consistent with the jwksUrl path on purpose.
That makes sense. The one place it can bite is when two different issuers happen to share signing keys; e.g. a multi-tenant IdP, or Festival Wristbands issued by two AuthConfigs pointing at the same signing-key Secret. In that case a token minted by issuer A can satisfy a config trusting issuer B, because it's only the signature being checked and the keys match. And it's quiet... the token lands as a valid identity, and nothing in the logs hints at it. Someone who hasn't added the authorization rule wouldn't know.
So this isn't a bug report: it's the RFE that came out of that discussion: give people a way to enforce the issuer directly, without needing a separate authorization rule.
The proposal:
authentication:
my-oidc:
jwt:
issuerUrl: https://issuer.example.com
checkIssuerClaim: true # new field, defaults to false
checkIssuerClaim defaults to false, so nothing changes for anyone on upgrade.
- When
true, Authorino rejects any token whose iss doesn't match issuerUrl, right at the authentication phase. (The discovered OIDC provider already knows the expected issuer, so it's basically letting the library do the check it was going to do anyway.)
- No effect on
jwksUrl, which has no expected issuer to compare against.
- To make the opt-in nature discoverable, an INFO log at reconciliation when an
issuerUrl config is used without checkIssuerClaim, plus a note in the docs.
I've got this working locally already (field + verifier change + docs + the reconciliation log), with tests covering both settings and a passing make e2e against real Keycloak confirming normal tokens still authenticate. Happy to open the PR right after this.
One thing I left out on purpose: there was also an idea for checkClientID / clientID for audience validation. That felt like a separate enough concern that I kept it out of this to keep the change focused.., glad to do it as a follow-up if that's wanted.
Thanks for @guicassolato for his help on this :)
While reading through the JWT authentication code, I noticed that when you configure
issuerUrl, Authorino verifies the token's signature and expiry.., but it never checks that the token'sissclaim actually matches the issuer it discovered. I asked about it, and it was confirmed this is intentional: issuer-checking is opt-in today, done via an authorization rule (CEL / pattern-matching / OPA), and kept consistent with thejwksUrlpath on purpose.That makes sense. The one place it can bite is when two different issuers happen to share signing keys; e.g. a multi-tenant IdP, or Festival Wristbands issued by two AuthConfigs pointing at the same signing-key Secret. In that case a token minted by issuer A can satisfy a config trusting issuer B, because it's only the signature being checked and the keys match. And it's quiet... the token lands as a valid identity, and nothing in the logs hints at it. Someone who hasn't added the authorization rule wouldn't know.
So this isn't a bug report: it's the RFE that came out of that discussion: give people a way to enforce the issuer directly, without needing a separate authorization rule.
The proposal:
checkIssuerClaimdefaults tofalse, so nothing changes for anyone on upgrade.true, Authorino rejects any token whoseissdoesn't matchissuerUrl, right at the authentication phase. (The discovered OIDC provider already knows the expected issuer, so it's basically letting the library do the check it was going to do anyway.)jwksUrl, which has no expected issuer to compare against.issuerUrlconfig is used withoutcheckIssuerClaim, plus a note in the docs.I've got this working locally already (field + verifier change + docs + the reconciliation log), with tests covering both settings and a passing
make e2eagainst real Keycloak confirming normal tokens still authenticate. Happy to open the PR right after this.One thing I left out on purpose: there was also an idea for
checkClientID/clientIDfor audience validation. That felt like a separate enough concern that I kept it out of this to keep the change focused.., glad to do it as a follow-up if that's wanted.Thanks for @guicassolato for his help on this :)