Skip to content

Commit

Permalink
Keycloak: Fix infinite loop in RemoteOidcMapper
Browse files Browse the repository at this point in the history
  • Loading branch information
thomasdarimont committed Sep 7, 2023
1 parent 262456a commit 8fe93b2
Showing 1 changed file with 35 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ public class RemoteOidcMapper extends AbstractOIDCProtocolMapper implements OIDC

private static final String CONFIG_ADD_AUTH_HEADER = "addAuthHeader";

private static final String CONFIG_INTERNAL_CLIENT_ID = "internalClientId";

private static final String DEFAULT_INTERNAL_CLIENT_ID = "admin-cli";

public static final String DEFAULT_REMOTE_CLAIM_URL = "https://id.acme.test:4543/api/users/claims?userId={userId}&username={username}&clientId={clientId}&issuer={issuer}";

public static final String ROOT_OBJECT = "$ROOT$";
Expand All @@ -90,7 +94,15 @@ public class RemoteOidcMapper extends AbstractOIDCProtocolMapper implements OIDC
.type(ProviderConfigProperty.BOOLEAN_TYPE)
.label("Add Authorization Header")
.helpText("If set to true, an dynamically generated access-token will added to the Authorization header of the request")
.defaultValue(false)
.defaultValue(true)
.add()

.property()
.name(CONFIG_INTERNAL_CLIENT_ID)
.type(ProviderConfigProperty.STRING_TYPE)
.label("Internal Client ID")
.helpText("The client_id to generate the internal access-token for the Authorization header. Defaults to admin-cli.")
.defaultValue(DEFAULT_INTERNAL_CLIENT_ID)
.add()

.build();
Expand Down Expand Up @@ -143,6 +155,13 @@ protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSes
issuer = Urls.realmIssuer(context.getUri().getBaseUri(), context.getRealm().getName());
}


var internalClientId = mappingModel.getConfig().getOrDefault(CONFIG_INTERNAL_CLIENT_ID, DEFAULT_INTERNAL_CLIENT_ID);
if (internalClientId.equals(clientId)) {
// workaround for infinite loop when generating remote claims into access-token.
return;
}

Object claimValue = fetchRemoteClaims(mappingModel, userSession, session, issuer, clientId);
LOGGER.infof("setClaim %s=%s", mappingModel.getName(), claimValue);

Expand All @@ -169,37 +188,35 @@ private boolean copyClaimsToRoot(Object claimValue, String claimName) {
private Object fetchRemoteClaims(ProtocolMapperModel mappingModel, UserSessionModel userSession, KeycloakSession session, String issuer, String clientId) {

try {
String url = createUri(mappingModel, userSession, issuer, clientId);
SimpleHttp http = SimpleHttp.doGet(url, session);
var url = createUri(mappingModel, userSession, issuer, clientId);
var http = SimpleHttp.doGet(url, session);

if (Boolean.parseBoolean(mappingModel.getConfig().getOrDefault(CONFIG_ADD_AUTH_HEADER, "false"))) {
var accessToken = createInternalAccessToken(userSession, session);
http.header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken);
}

addAuthHeaderIfNecessary(mappingModel, http, userSession, session);
try (var response = http.asResponse()) {

SimpleHttp.Response response = http.asResponse();
if (response.getStatus() != 200) {
log.warnf("Could not fetch remote claims for user. status=%s", response.getStatus());
return null;
}

if (response.getStatus() != 200) {
log.warnf("Could not fetch remote claims for user. status=%s", response.getStatus());
return null;
return response.asJson();
}

return response.asJson();
} catch (IOException e) {
log.warnf("Could not fetch remote claims for user. error=%s", e.getMessage());
}

return null;
}

protected void addAuthHeaderIfNecessary(ProtocolMapperModel mappingModel, SimpleHttp http, UserSessionModel userSession, KeycloakSession session) {

if (!Boolean.parseBoolean(mappingModel.getConfig().getOrDefault(CONFIG_ADD_AUTH_HEADER, "false"))) {
return;
}

String accessToken = TokenUtils.generateAccessToken(session, userSession, "admin-cli", "iam", token -> {
private String createInternalAccessToken(UserSessionModel userSession, KeycloakSession session) {
return TokenUtils.generateAccessToken(session, userSession, "admin-cli", "iam", token -> {
// mark this token request as an internal iam request
token.getOtherClaims().put("groups", List.of("iam"));
});
http.header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken);
}

protected String createUri(ProtocolMapperModel mappingModel, UserSessionModel userSession, String issuer, String clientId) {
Expand Down

0 comments on commit 8fe93b2

Please sign in to comment.