8000 Signed JWT - Issuer should not be required to match Subject · Issue #15688 · keycloak/keycloak · GitHub
[go: up one dir, main page]

Skip to content

Signed JWT - Issuer should not be required to match Subject #15688

Closed as not planned

Description

@michaelaird

Area

authorization-services

Describe the bug

See this same issue from nimbus: https://bitbucket.org/connect2id/oauth-2.0-sdk-with-openid-connect-extensions/issues/393/allow-issuer-and-subject-in-client-jwt

Currently it is required that if a client assertion is used for JWT client authentication, that the issuer and subject are the same.

See this PR: #7755 (JWTClientAuthticator line 108)

However, there is no such requirement in the standard https://datatracker.ietf.org/doc/html/rfc7523#section-3

Version

latest

Expected behavior

Sub and Iss claims should be independent.

Actual behavior

Authentication fails if they don't match.

How to Reproduce?

Use a JWT token with different iss/sub claims

Anything else?

No response

Activity

  1. jbman commented on Nov 29, 2022

    @jbman
    Contributor

    section 3. states

    1. The JWT MUST contain an "iss" (issuer) claim that contains a
        unique identifier for the entity that issued the JWT.  In the
        absence of an application profile specifying otherwise,
        compliant applications MUST compare issuer values [...]
    

    Using the client ID at (sub) itself as expected issuer seems to be one option, where the client needs to forge tokens on its own.
    If the client ID isn't used, some other issuer would need to be configured as expected for the client. right?
    It's not very clear from the spec, but if not why does it talk about comparing issuer values?

  2. michaelaird commented on Nov 30, 2022

    @michaelaird
    Author
  3. sschu commented on Nov 30, 2022

    @sschu
    Contributor

    If you don't require issuer and subject to be identical, you need a mechanism to configure which issuers to trust when it comes to making statements about clients. As such, I would say this is a feature request or an enhancement - not a bug.

  • michaelaird commented on Nov 30, 2022

    @michaelaird
    Author
  • sschu commented on Dec 1, 2022

    @sschu
    Contributor

    The spec does not say that the two might not be the same, so it clearly does not violate the spec.

  • michaelaird commented on Dec 1, 2022

    @michaelaird
    Author
  • added this to the milestone on Apr 4, 2023
  • mposolda commented on Apr 4, 2023

    @mposolda
    Contributor

    Thanks for the report, but unfortunately due the amount of other reported issues and other priorities, Keycloak team does not have time to properly triage this bug. So preliminary added to Backlog for now.
    It will be helpful if:

    • You can verify if still applicable in latest Keycloak released version. If not, then it is welcome to close this issue.
    • If you figure that this may not be a valid bug (for example just a mistake in configuration etc), it will be also welcome to close this issue
    • If still applicable in latest version, it will be welcome to add the comment as well, that this was still reproduced with latest Keycloak version as it is very valuable info. Anyone is welcome to comment with this or add other relevant comments to this issue.
  • cmiles74 commented on Apr 28, 2023

    @cmiles74
    Contributor

    I am curious if this is related to my issue (#19953)...

    I am required to provide an access token (which I generate with Keycloak) to a third party. This third party requires that the "sub" and "iss" claims be the same, however, this is difficult to do as Keycloak requires the "iss" be set to it's own URL.

    I will check with my third party, if they are using this authorization library then that might be where this requirement is coming from.

  • pedroigor commented on Mar 4, 2024

    @pedroigor
    Contributor

    This one also looks like an enhancement for me because we don't have mechanisms to trust only the values set in the trust claim. Doing that will require a different form of validation (e.g.: binding certs to trusted issuers).

    It might be related to #24512.

  • 5 remaining items

  • removed this from the milestone on Mar 4, 2024
  • keycloak-github-bot commented on Mar 4, 2024

    @keycloak-github-bot

    Due to the amount of issues reported by the community we are not able to prioritise resolving this issue at the moment.

    If you are affected by this issue, upvote it by adding a 👍 to the description. We would also welcome a contribution to fix the issue.

  • keycloak-github-bot commented on Jun 3, 2024

    @keycloak-github-bot

    Due to lack of updates in the last 180 days this issue will be automatically closed.

  • MatthiasValvekens commented on Jul 8, 2024

    @MatthiasValvekens

    I stumbled upon this while experimenting with Keycloak <> Kubernetes integrations. Essentially, I'd like to be able to have my Kubernetes service accounts authenticate to my Keycloak instance as Keycloak clients by using a service account token issued by the K8S cluster. That service account token is a signed JWT token, with the cluster's OIDC URL in the iss claim and the service account name in the sub claim. I'd love to be able to get this to work by configuring a validation policy along the lines of "for this client, trust issuer XYZ, and load the keys using OIDC key discovery" (potentially with TLS certificate pinning for the OIDC discovery service). This is, in a nutshell, also how you'd federate OIDC identities with AWS IAM.

    Reason: this would make it much easier to set up new services that integrate with Keycloak in my cluster, because I can use pretty much the same config for all of my clients, and I can delegate all key management tasks to the Kubernetes API.

    I can't imagine I'm the first person trying to set up something like this, but after some experimentation & googling I ended up here. Reading through the thread, I think the feature request made in the OP would address my use case as well.

    (In the meantime, I guess I'll look for ways to extend Keycloak's client authentication system, but I figured I'd at least drop the use case here for future reference)

  • timbrown5 commented on Jul 18, 2024

    @timbrown5

    We hit this too when trying to trust a signed GitHub ID Token and use that generate a Keycloak access token using the client flow.
    There is this stipulation in the spec, which I assume is where this comes from:

       2.   The JWT MUST contain a "sub" (subject) claim identifying the
            principal that is the subject of the JWT.  Two cases need to be
            differentiated:
     ...
            B.  For client authentication, the subject MUST be the
                "client_id" of the OAuth client.
    

    That said, I think the code is still wrong - it checks if(!clientId.equals(token.getIssuer())) { throw... } here

    Despite it referring to clientId it's actually (as the exception suggests) is comparing issuer to subject as String clientId = token.getSubject(); here.

    Should this be String clientId = context.getClient().getClientId(); instead?

    Then we might be able to get our use-case working by setting the client_id to the subject - which I can understand more.

  • eminwux commented on Sep 3, 2024

    @eminwux

    This is needed also when authenticating between Keycloak realms.

    For instance, this is the id_token I get when authenticating:

    {
        "acr": "1",
        "at_hash": "Mn1M3ZigbMmZqPuPSf6k8A",
        "aud": "confidential",
        "auth_time": 0,
        "azp": "confidential",
        "clientAddress": "172.16.1.20",
        "clientHost": "172.16.1.20",
        "client_id": "confidential",
        "email_verified": false,
        "exp": 1725393297,
        "iat": 1725392997,
        "iss": "https://core1.domain/realms/tenant",
        "jti": "8be09fbf-387d-4916-bc23-69156a9fa5cf",
        "preferred_username": "service-account-confidential",
        "sub": "0db5d38c-e8cb-46cc-98f7-919c9ea04753",
        "typ": "ID"
    }
    

    Here you can see that sub != iss.

    When I try to use this id_token on another client from a different realm, I get:

    2024-09-03 15:57:53,042 ERROR [org.keycloak.services] (executor-thread-10) KC-SERVICES0025: Error when validating client assertion: java.lang.RuntimeException: Issuer mismatch. The issuer should match the subject
    	at org.keycloak.authentication.authenticators.client.JWTClientValidator.validateClient(JWTClientValidator.java:115)
    	at org.keycloak.authentication.authenticators.client.JWTClientAuthenticator.authenticateClient(JWTClientAuthenticator.java:75)
    	at org.keycloak.authentication.ClientAuthenticationFlow.processFlow(ClientAuthenticationFlow.java:72)
    	at org.keycloak.authentication.AuthenticationProcessor.authenticateClient(AuthenticationProcessor.java:894)
    	at org.keycloak.protocol.oidc.utils.AuthorizeClientUtil.authorizeClient(AuthorizeClientUtil.java:50)
    	at org.keycloak.protocol.oidc.endpoints.TokenEndpoint.checkClient(TokenEndpoint.java:253)
    	at org.keycloak.protocol.oidc.endpoints.TokenEndpoint.processGrantRequest(TokenEndpoint.java:201)
    	at org.keycloak.protocol.oidc.endpoints.TokenEndpoint$quarkusrestinvoker$processGrantRequest_6408e15340992839b66447750c221d9aaa837bd7.invoke(Unknown Source)
    	at org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
    	at io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:141)
    	at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:145)
    	at io.quarkus.vertx.core.runtime.VertxCoreRecorder$14.runWith(VertxCoreRecorder.java:576)
    	at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2513)
    	at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1538)
    	at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
    	at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
    	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
    	at java.base/java.lang.Thread.run(Thread.java:840)
    

    I could not find yet a workaround for this.

  • eminwux commented on Sep 3, 2024

    @eminwux

    Anyone interested in following up on this issue, please see discussion 32626

  • yanxch commented on Feb 6, 2025

    @yanxch

    I think I have a similar problem during testing client credential flow with signed jwts.

    If I understand it correctly I can't use a JWT token from Keycloak itself (from eg a different realm) to test this behaviour because I can't change the issuer in the token, which must be equal to the subject,
    and the subject must be equal to the client_id in the request the contains the signed JWT.

    So I'm stuck.
    What if my external partner has an Keycloak as well. Then they can't used it to generate their signed JWT?
    I must be missing something.
    Can u help me?

    JWTClientValidator.java:
    
        public boolean validateClient() {
            if (token == null) throw new IllegalStateException("Incorrect usage. Variable 'token' is null. Need to read JWS first before validateClient");
    
            String clientId = token.getSubject();
            if (clientId == null) {
                throw new RuntimeException("Can't identify client. Subject missing on JWT token");
            }
    
            if (!clientId.equals(token.getIssuer())) {
                throw new RuntimeException("Issuer mismatch. The issuer should match the subject");
            }
    
            String clientIdParam = params.getFirst(OAuth2Constants.CLIENT_ID);
            if (clientIdParam != null && !clientIdParam.equals(clientId)) {
                throw new RuntimeException("client_id parameter not matching with client from JWT token");
            }
    
  • Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Metadata

    Metadata

    Assignees

    No one assigned

      Type

      No type

      Fields

      No fields configured for issues without a type.

      Projects

      No projects

        Milestone

        No milestone

        Relationships

        None yet

        Development

        No branches or pull requests

        Issue actions

        0