Skip to content

Commit

Permalink
Disable issuer verification of Initial Access Token and Registration …
Browse files Browse the repository at this point in the history
…Access Token

Signed-off-by: Réda Housni Alaoui <[email protected]>
  • Loading branch information
reda-alaoui committed Mar 19, 2024
1 parent b77e228 commit 531d1fc
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

package org.keycloak.services.clientregistration;

import java.util.function.Predicate;
import org.jboss.resteasy.spi.Failure;
import org.keycloak.Config;
import org.keycloak.OAuthErrorException;
Expand Down Expand Up @@ -89,7 +90,12 @@ private void init() {

token = split[1];

ClientRegistrationTokenUtils.TokenVerification tokenVerification = ClientRegistrationTokenUtils.verifyToken(session, realm, token);
ClientRegistrationTokenUtils.TokenVerification tokenVerification = ClientRegistrationTokenUtils.verifyToken(
session,
realm,
token,
Predicate.<JsonWebToken>not(ClientRegistrationAuth::isRegistrationAccessToken)
.and(Predicate.not(ClientRegistrationAuth::isInitialAccessToken)));
if (tokenVerification.getError() != null) {
throw unauthorized(tokenVerification.getError().getMessage());
}
Expand Down Expand Up @@ -121,10 +127,19 @@ private boolean isBearerToken() {
}

public boolean isInitialAccessToken() {
return isInitialAccessToken(jwt);
}

private static boolean isInitialAccessToken(JsonWebToken jwt) {
return jwt != null && ClientRegistrationTokenUtils.TYPE_INITIAL_ACCESS_TOKEN.equals(jwt.getType());
}


public boolean isRegistrationAccessToken() {
return isRegistrationAccessToken(jwt);
}

private static boolean isRegistrationAccessToken(JsonWebToken jwt) {
return jwt != null && ClientRegistrationTokenUtils.TYPE_REGISTRATION_ACCESS_TOKEN.equals(jwt.getType());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

package org.keycloak.services.clientregistration;

import java.util.function.Predicate;
import org.keycloak.TokenCategory;
import org.keycloak.TokenVerifier;
import org.keycloak.common.VerificationException;
Expand Down Expand Up @@ -85,16 +86,17 @@ public static String createInitialAccessToken(KeycloakSession session, RealmMode
return setupToken(initialToken, session, realm, model.getId(), TYPE_INITIAL_ACCESS_TOKEN, model.getExpiration() > 0 ? model.getTimestamp() + model.getExpiration() : 0);
}

public static TokenVerification verifyToken(KeycloakSession session, RealmModel realm, String token) {
public static TokenVerification verifyToken(KeycloakSession session, RealmModel realm, String token, Predicate<JsonWebToken> mustVerifyIssuer) {
if (token == null) {
return TokenVerification.error(new RuntimeException("Missing token"));
}

String kid;
JsonWebToken jwt;
try {
TokenVerifier.Predicate<JsonWebToken> realmUrlCheck = jsonWebToken -> !mustVerifyIssuer.test(jsonWebToken) || new TokenVerifier.RealmUrlCheck(getIssuer(session, realm)).test(jsonWebToken);
TokenVerifier<JsonWebToken> verifier = TokenVerifier.create(token, JsonWebToken.class)
.withChecks(new TokenVerifier.RealmUrlCheck(getIssuer(session, realm)), TokenVerifier.IS_ACTIVE, new TokenRevocationCheck(session));
.withChecks(realmUrlCheck, TokenVerifier.IS_ACTIVE, new TokenRevocationCheck(session));

SignatureVerifierContext verifierContext = session.getProvider(SignatureProvider.class, verifier.getHeader().getAlgorithm().name()).verifier(verifier.getHeader().getKeyId());
verifier.verifierContext(verifierContext);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -753,4 +753,24 @@ public void registerClientWithWrongCharacters() throws IOException {
}
}
}

@Test
public void registerClientInMasterRealmViaAlternativeDomain() throws Exception {
String alternativeContextRoot = "http://keycloak.127.0.0.1.nip.io:" + System.getProperty("auth.server.http.port");
ClientRegistration alternativeReg = ClientRegistration.create().url(alternativeContextRoot + "/auth", "master").build();

String token = oauth.doGrantAccessTokenRequest("master", "admin", "admin", null, Constants.ADMIN_CLI_CLIENT_ID, null).getAccessToken();
alternativeReg.auth(Auth.token(token));

ClientRepresentation client = new ClientRepresentation();
client.setClientId(CLIENT_ID);
client.setSecret(CLIENT_SECRET);

try {
alternativeReg.create(client);
fail("Expected 401");
} catch (ClientRegistrationException e) {
assertEquals(401, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.junit.Test;
import org.keycloak.admin.client.resource.ClientInitialAccessResource;
import org.keycloak.client.registration.Auth;
import org.keycloak.client.registration.ClientRegistration;
import org.keycloak.client.registration.ClientRegistrationException;
import org.keycloak.client.registration.HttpErrorException;
import org.keycloak.crypto.Algorithm;
Expand Down Expand Up @@ -150,4 +151,20 @@ public void createDeleted() throws ClientRegistrationException, InterruptedExcep
}
}

@Test
public void createViaAlternativeDomain() throws ClientRegistrationException {
String alternativeContextRoot = "http://keycloak.127.0.0.1.nip.io:" + System.getProperty("auth.server.http.port");
ClientRegistration alternativeReg = ClientRegistration.create().url(alternativeContextRoot + "/auth", "test").build();

ClientInitialAccessPresentation response = resource.create(new ClientInitialAccessCreatePresentation());
alternativeReg.auth(Auth.token(response));

ClientRepresentation rep = new ClientRepresentation();

setTimeOffset(10);

ClientRepresentation created = alternativeReg.create(rep);
Assert.assertNotNull(created);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.junit.Before;
import org.junit.Test;
import org.keycloak.client.registration.Auth;
import org.keycloak.client.registration.ClientRegistration;
import org.keycloak.client.registration.ClientRegistrationException;
import org.keycloak.client.registration.HttpErrorException;
import org.keycloak.representations.idm.ClientRepresentation;
Expand Down Expand Up @@ -62,22 +63,26 @@ public void before() throws Exception {
reg.auth(Auth.token(client.getRegistrationAccessToken()));
}

private ClientRepresentation assertRead(ClientRegistration reg, String id, String registrationAccess, boolean expectSuccess) throws ClientRegistrationException {
if (expectSuccess) {
reg.auth(Auth.token(registrationAccess));
ClientRepresentation rep = reg.get(id);
assertNotNull(rep);
return rep;
} else {
reg.auth(Auth.token(registrationAccess));
try {
reg.get(client.getClientId());
fail("Expected 403");
} catch (Exception e) {
assertEquals(401, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
}
}
return null;
}

private ClientRepresentation assertRead(String id, String registrationAccess, boolean expectSuccess) throws ClientRegistrationException {
if (expectSuccess) {
reg.auth(Auth.token(registrationAccess));
ClientRepresentation rep = reg.get(id);
assertNotNull(rep);
return rep;
} else {
reg.auth(Auth.token(registrationAccess));
try {
reg.get(client.getClientId());
fail("Expected 403");
} catch (Exception e) {
assertEquals(401, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
}
}
return null;
return assertRead(reg, id, registrationAccess, expectSuccess);
}

@Test
Expand Down Expand Up @@ -174,4 +179,13 @@ public void deleteClientWithBadRegistrationToken() throws ClientRegistrationExce
assertNotNull(getClient(client.getId()));
}

@Test
public void getClientViaAlternativeDomain() throws ClientRegistrationException {
setTimeOffset(10);

String alternativeContextRoot = "http://keycloak.127.0.0.1.nip.io:" + System.getProperty("auth.server.http.port");
ClientRegistration alternativeReg = ClientRegistration.create().url(alternativeContextRoot + "/auth", "test").build();

assertRead(alternativeReg, client.getClientId(), client.getRegistrationAccessToken(), true);
}
}

0 comments on commit 531d1fc

Please sign in to comment.