KEYCLOAK-3424 Issuer or token-endpoint as audience in signed JWT

This commit is contained in:
mposolda 2016-08-29 14:02:43 +02:00
parent a7f9a6e095
commit f4aee129e4
2 changed files with 22 additions and 5 deletions

View file

@ -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()) {

View file

@ -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);