From f4aee129e4bd701d1a89de2278e85d8bfd48930f Mon Sep 17 00:00:00 2001 From: mposolda Date: Mon, 29 Aug 2016 14:02:43 +0200 Subject: [PATCH] KEYCLOAK-3424 Issuer or token-endpoint as audience in signed JWT --- .../client/JWTClientAuthenticator.java | 11 +++++++---- .../client/OIDCClientRegistrationTest.java | 16 +++++++++++++++- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/client/JWTClientAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/client/JWTClientAuthenticator.java index 5139e3a61d..789d38111c 100644 --- a/services/src/main/java/org/keycloak/authentication/authenticators/client/JWTClientAuthenticator.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/client/JWTClientAuthenticator.java @@ -30,6 +30,7 @@ import java.util.Set; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriBuilder; import org.keycloak.OAuth2Constants; import org.keycloak.authentication.AuthenticationFlowError; @@ -42,6 +43,7 @@ import org.keycloak.models.ClientModel; import org.keycloak.models.RealmModel; import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.protocol.oidc.OIDCLoginProtocol; +import org.keycloak.protocol.oidc.OIDCLoginProtocolService; import org.keycloak.provider.ProviderConfigProperty; import org.keycloak.representations.JsonWebToken; import org.keycloak.representations.idm.CertificateRepresentation; @@ -139,10 +141,11 @@ public class JWTClientAuthenticator extends AbstractClientAuthenticator { throw new RuntimeException("Signature on JWT token failed validation"); } - // Validate other things - String expectedAudience = Urls.realmIssuer(context.getUriInfo().getBaseUri(), realm.getName()); - if (!token.hasAudience(expectedAudience)) { - throw new RuntimeException("Token audience doesn't match domain. Realm audience is '" + expectedAudience + "' but audience from token is '" + Arrays.asList(token.getAudience()).toString() + "'"); + // Allow both "issuer" or "token-endpoint" as audience + String issuerUrl = Urls.realmIssuer(context.getUriInfo().getBaseUri(), realm.getName()); + String tokenUrl = OIDCLoginProtocolService.tokenUrl(context.getUriInfo().getBaseUriBuilder()).build(realm.getName()).toString(); + if (!token.hasAudience(issuerUrl) && !token.hasAudience(tokenUrl)) { + throw new RuntimeException("Token audience doesn't match domain. Realm issuer is '" + issuerUrl + "' but audience from token is '" + Arrays.asList(token.getAudience()).toString() + "'"); } if (!token.isActive()) { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCClientRegistrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCClientRegistrationTest.java index 5d7d2c14f0..1c6b85b80c 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCClientRegistrationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCClientRegistrationTest.java @@ -37,8 +37,10 @@ import org.keycloak.constants.ServiceUrlConstants; import org.keycloak.jose.jwk.JSONWebKeySet; import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.protocol.oidc.OIDCLoginProtocol; +import org.keycloak.protocol.oidc.OIDCLoginProtocolService; import org.keycloak.protocol.oidc.utils.OIDCResponseType; import org.keycloak.representations.AccessToken; +import org.keycloak.representations.JsonWebToken; import org.keycloak.representations.idm.ClientInitialAccessCreatePresentation; import org.keycloak.representations.idm.ClientInitialAccessPresentation; import org.keycloak.representations.idm.ClientRegistrationTrustedHostRepresentation; @@ -53,6 +55,7 @@ import java.util.LinkedList; import java.util.List; import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriBuilder; import static org.junit.Assert.*; import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson; @@ -260,7 +263,18 @@ public class OIDCClientRegistrationTest extends AbstractClientRegistrationTest { PrivateKey privateKey = KeycloakModelUtils.getPrivateKey(PRIVATE_KEY); - JWTClientCredentialsProvider jwtProvider = new JWTClientCredentialsProvider(); + // Use token-endpoint as audience as OIDC conformance testsuite is using it too. + JWTClientCredentialsProvider jwtProvider = new JWTClientCredentialsProvider() { + + @Override + protected JsonWebToken createRequestToken(String clientId, String realmInfoUrl) { + JsonWebToken jwt = super.createRequestToken(clientId, realmInfoUrl); + String tokenEndpointUrl = OIDCLoginProtocolService.tokenUrl(UriBuilder.fromUri(getAuthServerRoot())).build(REALM_NAME).toString(); + jwt.audience(tokenEndpointUrl); + return jwt; + } + + }; jwtProvider.setPrivateKey(privateKey); jwtProvider.setTokenTimeout(10); return jwtProvider.createSignedRequestToken(clientId, realmInfoUrl);