Broker claim mapper not recognizing claims from user info endpoint
Closes #12137
This commit is contained in:
parent
2eb37dbe4f
commit
bc31fde4c0
2 changed files with 80 additions and 6 deletions
|
@ -83,7 +83,7 @@ public abstract class AbstractClaimMapper extends AbstractIdentityProviderMapper
|
||||||
}
|
}
|
||||||
{ // search ID Token
|
{ // search ID Token
|
||||||
Object rawIdToken = context.getContextData().get(OIDCIdentityProvider.VALIDATED_ID_TOKEN);
|
Object rawIdToken = context.getContextData().get(OIDCIdentityProvider.VALIDATED_ID_TOKEN);
|
||||||
JsonWebToken idToken;
|
JsonWebToken idToken = null;
|
||||||
|
|
||||||
if (rawIdToken instanceof String) {
|
if (rawIdToken instanceof String) {
|
||||||
try {
|
try {
|
||||||
|
@ -93,13 +93,13 @@ public abstract class AbstractClaimMapper extends AbstractIdentityProviderMapper
|
||||||
}
|
}
|
||||||
} else if (rawIdToken instanceof JsonWebToken) {
|
} else if (rawIdToken instanceof JsonWebToken) {
|
||||||
idToken = (JsonWebToken) rawIdToken;
|
idToken = (JsonWebToken) rawIdToken;
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Object value = getClaimValue(idToken, claim);
|
if (idToken != null) {
|
||||||
if (value != null)
|
Object value = getClaimValue(idToken, claim);
|
||||||
return value;
|
if (value != null)
|
||||||
|
return value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
// Search the OIDC UserInfo claim set (if any)
|
// Search the OIDC UserInfo claim set (if any)
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.testsuite.adapter.servlet;
|
package org.keycloak.testsuite.adapter.servlet;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
import org.jboss.arquillian.container.test.api.Deployment;
|
import org.jboss.arquillian.container.test.api.Deployment;
|
||||||
import org.jboss.arquillian.container.test.api.OperateOnDeployment;
|
import org.jboss.arquillian.container.test.api.OperateOnDeployment;
|
||||||
import org.jboss.arquillian.graphene.page.Page;
|
import org.jboss.arquillian.graphene.page.Page;
|
||||||
|
@ -25,27 +26,35 @@ import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.OAuth2Constants;
|
import org.keycloak.OAuth2Constants;
|
||||||
|
import org.keycloak.admin.client.resource.ClientResource;
|
||||||
import org.keycloak.admin.client.resource.RealmResource;
|
import org.keycloak.admin.client.resource.RealmResource;
|
||||||
import org.keycloak.authorization.model.Policy;
|
import org.keycloak.authorization.model.Policy;
|
||||||
import org.keycloak.authorization.model.ResourceServer;
|
import org.keycloak.authorization.model.ResourceServer;
|
||||||
import org.keycloak.broker.oidc.OIDCIdentityProviderConfig;
|
import org.keycloak.broker.oidc.OIDCIdentityProviderConfig;
|
||||||
|
import org.keycloak.broker.oidc.mappers.UserAttributeMapper;
|
||||||
import org.keycloak.common.Profile;
|
import org.keycloak.common.Profile;
|
||||||
import org.keycloak.exportimport.ExportImportConfig;
|
import org.keycloak.exportimport.ExportImportConfig;
|
||||||
import org.keycloak.exportimport.singlefile.SingleFileExportProviderFactory;
|
import org.keycloak.exportimport.singlefile.SingleFileExportProviderFactory;
|
||||||
import org.keycloak.jose.jws.JWSInput;
|
import org.keycloak.jose.jws.JWSInput;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.Constants;
|
import org.keycloak.models.Constants;
|
||||||
|
import org.keycloak.models.IdentityProviderMapperModel;
|
||||||
|
import org.keycloak.models.IdentityProviderMapperSyncMode;
|
||||||
import org.keycloak.models.IdentityProviderModel;
|
import org.keycloak.models.IdentityProviderModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.protocol.oidc.OIDCConfigAttributes;
|
import org.keycloak.protocol.oidc.OIDCConfigAttributes;
|
||||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||||
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
|
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
|
||||||
|
import org.keycloak.protocol.oidc.mappers.HardcodedClaim;
|
||||||
|
import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
|
||||||
import org.keycloak.representations.AccessToken;
|
import org.keycloak.representations.AccessToken;
|
||||||
import org.keycloak.representations.AccessTokenResponse;
|
import org.keycloak.representations.AccessTokenResponse;
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.representations.idm.FederatedIdentityRepresentation;
|
import org.keycloak.representations.idm.FederatedIdentityRepresentation;
|
||||||
|
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
|
||||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||||
|
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.representations.idm.RoleRepresentation;
|
import org.keycloak.representations.idm.RoleRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
|
@ -762,6 +771,71 @@ public class BrokerLinkAndTokenExchangeTest extends AbstractServletsAdapterTest
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExternalExchangeCreateNewUserUsingMappers() throws Exception {
|
||||||
|
RealmResource parentRealm = adminClient.realms().realm(PARENT_IDP);
|
||||||
|
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, "claim-from-idp");
|
||||||
|
config.put(HardcodedClaim.CLAIM_VALUE, "true");
|
||||||
|
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
|
||||||
|
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, "true");
|
||||||
|
claimMapper.setConfig(config);
|
||||||
|
ClientRepresentation client = parentRealm.clients().findByClientId(PARENT_CLIENT).get(0);
|
||||||
|
ClientResource clientResource = parentRealm.clients().get(client.getId());
|
||||||
|
clientResource.getProtocolMappers().createMapper(claimMapper).close();
|
||||||
|
|
||||||
|
RealmResource childRealm = adminClient.realms().realm(CHILD_IDP);
|
||||||
|
IdentityProviderMapperRepresentation attributeMapper = new IdentityProviderMapperRepresentation();
|
||||||
|
attributeMapper.setName("attribute-mapper");
|
||||||
|
attributeMapper.setIdentityProviderMapper(UserAttributeMapper.PROVIDER_ID);
|
||||||
|
attributeMapper.setIdentityProviderAlias(PARENT_IDP);
|
||||||
|
attributeMapper.setConfig(ImmutableMap.<String,String>builder()
|
||||||
|
.put(IdentityProviderMapperModel.SYNC_MODE, IdentityProviderMapperSyncMode.INHERIT.toString())
|
||||||
|
.put(UserAttributeMapper.CLAIM, "claim-from-idp")
|
||||||
|
.put(UserAttributeMapper.USER_ATTRIBUTE, "claim-to-broker")
|
||||||
|
.build());
|
||||||
|
childRealm.identityProviders().get(PARENT_IDP).addMapper(attributeMapper).close();
|
||||||
|
|
||||||
|
String idToken = oauth.doGrantAccessTokenRequest(PARENT_IDP, PARENT3_USERNAME, "password", null, PARENT_CLIENT, "password").getAccessToken();
|
||||||
|
Assert.assertEquals(0, adminClient.realm(CHILD_IDP).getClientSessionStats().size());
|
||||||
|
|
||||||
|
try (Client httpClient = AdminClientUtil.createResteasyClient()) {
|
||||||
|
WebTarget exchangeUrl = childTokenExchangeWebTarget(httpClient);
|
||||||
|
IdentityProviderRepresentation rep = adminClient.realm(CHILD_IDP).identityProviders().get(PARENT_IDP).toRepresentation();
|
||||||
|
rep.getConfig().put(OIDCIdentityProviderConfig.VALIDATE_SIGNATURE, String.valueOf(false));
|
||||||
|
adminClient.realm(CHILD_IDP).identityProviders().get(PARENT_IDP).update(rep);
|
||||||
|
|
||||||
|
AccessToken token;
|
||||||
|
try (Response response = exchangeUrl.request()
|
||||||
|
.header(HttpHeaders.AUTHORIZATION, BasicAuthHelper.createHeader(ClientApp.DEPLOYMENT_NAME, "password"))
|
||||||
|
.post(Entity.form(
|
||||||
|
new Form()
|
||||||
|
.param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.TOKEN_EXCHANGE_GRANT_TYPE)
|
||||||
|
.param(OAuth2Constants.SUBJECT_TOKEN, idToken)
|
||||||
|
.param(OAuth2Constants.SUBJECT_TOKEN_TYPE, OAuth2Constants.JWT_TOKEN_TYPE)
|
||||||
|
.param(OAuth2Constants.SUBJECT_ISSUER, PARENT_IDP)
|
||||||
|
.param(OAuth2Constants.SCOPE, OAuth2Constants.SCOPE_OPENID)
|
||||||
|
))) {
|
||||||
|
Assert.assertEquals(200, response.getStatus());
|
||||||
|
|
||||||
|
AccessTokenResponse tokenResponse = response.readEntity(AccessTokenResponse.class);
|
||||||
|
JWSInput jws = new JWSInput(tokenResponse.getToken());
|
||||||
|
token = jws.readJsonContent(AccessToken.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
UserRepresentation newUser = childRealm.users().search(PARENT3_USERNAME).get(0);
|
||||||
|
Assert.assertNotNull(newUser.getAttributes());
|
||||||
|
Assert.assertTrue(newUser.getAttributes().containsKey("claim-to-broker"));
|
||||||
|
|
||||||
|
// cleanup remove the user
|
||||||
|
childRealm.users().get(token.getSubject()).remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void logoutAll() {
|
public void logoutAll() {
|
||||||
adminClient.realm(CHILD_IDP).logoutAll();
|
adminClient.realm(CHILD_IDP).logoutAll();
|
||||||
adminClient.realm(PARENT_IDP).logoutAll();
|
adminClient.realm(PARENT_IDP).logoutAll();
|
||||||
|
|
Loading…
Reference in a new issue