Parsing response from user info rather than the access token

Closes #22581
This commit is contained in:
Pedro Igor
2023-08-22 18:47:11 -03:00
committed by Marek Posolda
parent 43668afb71
commit b779df6a55
2 changed files with 84 additions and 1 deletions
@@ -496,7 +496,7 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
if (MediaType.APPLICATION_JSON_TYPE.isCompatible(contentMediaType)) {
userInfo = response.asJson();
} else if (APPLICATION_JWT_TYPE.isCompatible(contentMediaType)) {
userInfo = JsonSerialization.readValue(parseTokenInput(accessToken, false), JsonNode.class);
userInfo = JsonSerialization.readValue(parseTokenInput(response.asString(), false), JsonNode.class);
} else {
throw new RuntimeException("Unsupported content-type [" + contentType + "] in response from [" + userInfoUrl + "].");
}
@@ -16,18 +16,45 @@
*/
package org.keycloak.testsuite.broker;
import static java.util.Optional.ofNullable;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import com.google.common.collect.ImmutableMap;
import org.junit.Test;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.admin.client.resource.IdentityProviderResource;
import org.keycloak.admin.client.resource.ProtocolMappersResource;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.admin.client.resource.UsersResource;
import org.keycloak.broker.oidc.mappers.UserAttributeMapper;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.crypto.Algorithm;
import org.keycloak.crypto.KeyUse;
import org.keycloak.jose.jwe.JWEConstants;
import org.keycloak.keys.KeyProvider;
import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.IdentityProviderMapperSyncMode;
import org.keycloak.models.utils.DefaultKeyProviders;
import org.keycloak.protocol.ProtocolMapperUtils;
import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper;
import org.keycloak.protocol.oidc.OIDCConfigAttributes;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.mappers.HardcodedClaim;
import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
import org.keycloak.protocol.oidc.mappers.UserPropertyMapper;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.ComponentExportRepresentation;
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
/**
* <p>Tests the broker using a JWE encrypted token for id token and user info. The test
@@ -114,4 +141,60 @@ public class KcOidcBrokerJWETest extends AbstractBrokerTest {
}
};
}
@Test
public void testIdentityClaimsFromUserInfoEndpoint() {
configureUserInfoEndpointMappers();
testLogInAsUserInIDP();
UsersResource users = realmsResouce().realm(bc.consumerRealmName()).users();
List<UserRepresentation> usersRep = users.search(bc.getUserLogin(), true);
assertFalse(usersRep.isEmpty());
UserRepresentation userRep = usersRep.get(0);
List<String> expectedAttribute = ofNullable(userRep.getAttributes()).orElse(Map.of()).getOrDefault("user-info", List.of());
assertFalse(expectedAttribute.isEmpty());
assertEquals("true", expectedAttribute.get(0));
}
private void configureUserInfoEndpointMappers() {
RealmResource providerRealm = realmsResouce().realm(bc.providerRealmName());
ClientRepresentation client = providerRealm.clients().findByClientId(bc.getIDPClientIdInProviderRealm()).get(0);
ProtocolMapperRepresentation claimMapper = new ProtocolMapperRepresentation();
claimMapper.setName("custom-claim-hardcoded-mapper");
claimMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
claimMapper.setProtocolMapper(HardcodedClaim.PROVIDER_ID);
Map<String, String> config = new HashMap<>();
config.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, "user-info");
config.put(HardcodedClaim.CLAIM_VALUE, "true");
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, "true");
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "false");
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "false");
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_RESPONSE, "false");
claimMapper.setConfig(config);
ClientResource clientResource = providerRealm.clients().get(client.getId());
ProtocolMappersResource protocolMappers = clientResource.getProtocolMappers();
List<ProtocolMapperRepresentation> mappers = protocolMappers.getMappersPerProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
ProtocolMapperRepresentation mapper = mappers.stream().filter(new Predicate<ProtocolMapperRepresentation>() {
@Override
public boolean test(ProtocolMapperRepresentation mapper) {
return UserPropertyMapper.PROVIDER_ID.equals(mapper.getProtocolMapper())
&& mapper.getConfig().getOrDefault(ProtocolMapperUtils.USER_ATTRIBUTE, "").equals("email");
}
}).findAny().orElse(null);
clientResource.getProtocolMappers().delete(mapper.getId());
client.getProtocolMappers().add(claimMapper);
clientResource.update(client);
IdentityProviderResource idp = realmsResouce().realm(bc.consumerRealmName()).identityProviders().get(bc.getIDPAlias());
IdentityProviderMapperRepresentation attributeMapper = new IdentityProviderMapperRepresentation();
attributeMapper.setName("attribute-mapper");
attributeMapper.setIdentityProviderMapper(UserAttributeMapper.PROVIDER_ID);
attributeMapper.setIdentityProviderAlias(bc.getIDPAlias());
attributeMapper.setConfig(ImmutableMap.<String,String>builder()
.put(IdentityProviderMapperModel.SYNC_MODE, IdentityProviderMapperSyncMode.INHERIT.toString())
.put(UserAttributeMapper.CLAIM, "user-info")
.put(UserAttributeMapper.USER_ATTRIBUTE, "user-info")
.build());
idp.addMapper(attributeMapper);
}
}