diff --git a/core/src/main/java/org/keycloak/representations/adapters/config/AdapterConfig.java b/core/src/main/java/org/keycloak/representations/adapters/config/AdapterConfig.java index e2fe9fdcf9..c16516136e 100755 --- a/core/src/main/java/org/keycloak/representations/adapters/config/AdapterConfig.java +++ b/core/src/main/java/org/keycloak/representations/adapters/config/AdapterConfig.java @@ -18,7 +18,7 @@ import org.codehaus.jackson.annotate.JsonPropertyOrder; "allow-any-hostname", "disable-trust-manager", "truststore", "truststore-password", "client-keystore", "client-keystore-password", "client-key-password", "auth-server-url-for-backend-requests", "always-refresh-token", - "register-node-at-startup", "register-node-period", "token-store" + "register-node-at-startup", "register-node-period", "token-store", "principal-attribute" }) public class AdapterConfig extends BaseAdapterConfig { @@ -48,6 +48,8 @@ public class AdapterConfig extends BaseAdapterConfig { protected int registerNodePeriod = -1; @JsonProperty("token-store") protected String tokenStore; + @JsonProperty("principal-attribute") + protected String principalAttribute; public boolean isAllowAnyHostname() { return allowAnyHostname; @@ -152,4 +154,12 @@ public class AdapterConfig extends BaseAdapterConfig { public void setTokenStore(String tokenStore) { this.tokenStore = tokenStore; } + + public String getPrincipalAttribute() { + return principalAttribute; + } + + public void setPrincipalAttribute(String principalAttribute) { + this.principalAttribute = principalAttribute; + } } diff --git a/docbook/reference/en/en-US/modules/adapter-config.xml b/docbook/reference/en/en-US/modules/adapter-config.xml index 14c8f293a3..216095fdeb 100755 --- a/docbook/reference/en/en-US/modules/adapter-config.xml +++ b/docbook/reference/en/en-US/modules/adapter-config.xml @@ -336,6 +336,15 @@ + + principal-attribute + + + OpenID Connection ID Token attribute to populate the UserPrincipal name with. If token attribute is null, defaults to sub + Possible values are sub, preferred_username, email, name, nickname, given_name, family_name. + + + diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/AdapterUtils.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/AdapterUtils.java old mode 100644 new mode 100755 index 6e8b97caa1..10d6630339 --- a/integration/adapter-core/src/main/java/org/keycloak/adapters/AdapterUtils.java +++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/AdapterUtils.java @@ -59,4 +59,27 @@ public class AdapterUtils { } return roles; } + + public static String getPrincipalName(KeycloakDeployment deployment, AccessToken token) { + String attr = "sub"; + if (deployment.getPrincipalAttribute() != null) attr = deployment.getPrincipalAttribute(); + String name = null; + if ("sub".equals(attr)) { + name = token.getSubject(); + } else if ("email".equals(attr)) { + name = token.getEmail(); + } else if ("preferred_username".equals(attr)) { + name = token.getPreferredUsername(); + } else if ("name".equals(attr)) { + name = token.getName(); + } else if ("given_name".equals(attr)) { + name = token.getGivenName(); + } else if ("family_name".equals(attr)) { + name = token.getFamilyName(); + } else if ("nickname".equals(attr)) { + name = token.getNickName(); + } + if (name == null) name = token.getSubject(); + return name; + } } diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/CookieTokenStore.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/CookieTokenStore.java index 2f0956150b..8a35572097 100755 --- a/integration/adapter-core/src/main/java/org/keycloak/adapters/CookieTokenStore.java +++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/CookieTokenStore.java @@ -68,7 +68,7 @@ public class CookieTokenStore { log.debug("Token Verification succeeded!"); RefreshableKeycloakSecurityContext secContext = new RefreshableKeycloakSecurityContext(deployment, tokenStore, accessTokenString, accessToken, idTokenString, idToken, refreshTokenString); - return new KeycloakPrincipal(accessToken.getSubject(), secContext); + return new KeycloakPrincipal(AdapterUtils.getPrincipalName(deployment, accessToken), secContext); } catch (VerificationException ve) { log.warn("Failed verify token", ve); return null; diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java index 2380684f9e..fdd295e1d6 100755 --- a/integration/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java +++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java @@ -34,6 +34,7 @@ public class KeycloakDeployment { protected String accountUrl; protected String registerNodeUrl; protected String unregisterNodeUrl; + protected String principalAttribute = "sub"; protected String resourceName; protected boolean bearerOnly; @@ -333,4 +334,12 @@ public class KeycloakDeployment { public void setRegisterNodePeriod(int registerNodePeriod) { this.registerNodePeriod = registerNodePeriod; } + + public String getPrincipalAttribute() { + return principalAttribute; + } + + public void setPrincipalAttribute(String principalAttribute) { + this.principalAttribute = principalAttribute; + } } diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java index c79d90568d..8d8eed955c 100755 --- a/integration/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java +++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java @@ -54,6 +54,7 @@ public class KeycloakDeploymentBuilder { } else { deployment.setTokenStore(TokenStore.SESSION); } + if (adapterConfig.getPrincipalAttribute() != null) deployment.setPrincipalAttribute(adapterConfig.getPrincipalAttribute()); deployment.setResourceCredentials(adapterConfig.getCredentials()); deployment.setPublicClient(adapterConfig.isPublicClient()); deployment.setUseResourceRoleMappings(adapterConfig.isUseResourceRoleMappings()); diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/RequestAuthenticator.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/RequestAuthenticator.java index a4da6a2842..d5c7119821 100755 --- a/integration/adapter-core/src/main/java/org/keycloak/adapters/RequestAuthenticator.java +++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/RequestAuthenticator.java @@ -106,7 +106,7 @@ public abstract class RequestAuthenticator { protected void completeAuthentication(OAuthRequestAuthenticator oauth) { RefreshableKeycloakSecurityContext session = new RefreshableKeycloakSecurityContext(deployment, tokenStore, oauth.getTokenString(), oauth.getToken(), oauth.getIdTokenString(), oauth.getIdToken(), oauth.getRefreshToken()); - final KeycloakPrincipal principal = new KeycloakPrincipal(oauth.getToken().getSubject(), session); + final KeycloakPrincipal principal = new KeycloakPrincipal(AdapterUtils.getPrincipalName(deployment, oauth.getToken()), session); completeOAuthAuthentication(principal); } @@ -116,7 +116,7 @@ public abstract class RequestAuthenticator { protected void completeAuthentication(BearerTokenRequestAuthenticator bearer) { RefreshableKeycloakSecurityContext session = new RefreshableKeycloakSecurityContext(deployment, null, bearer.getTokenString(), bearer.getToken(), null, null, null); - final KeycloakPrincipal principal = new KeycloakPrincipal(bearer.getToken().getSubject(), session); + final KeycloakPrincipal principal = new KeycloakPrincipal(AdapterUtils.getPrincipalName(deployment, bearer.getToken()), session); completeBearerAuthentication(principal); } diff --git a/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/SharedAttributeDefinitons.java b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/SharedAttributeDefinitons.java index 93e5e86d6b..f19d4d0ee9 100755 --- a/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/SharedAttributeDefinitons.java +++ b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/SharedAttributeDefinitons.java @@ -130,6 +130,42 @@ public class SharedAttributeDefinitons { .setAllowExpression(true) .setDefaultValue(new ModelNode(false)) .build(); + protected static final SimpleAttributeDefinition AUTH_SERVER_URL_FOR_BACKEND_REQUESTS = + new SimpleAttributeDefinitionBuilder("auth-server-url-for-backend-requests", ModelType.STRING, true) + .setXmlName("auth-server-url-for-backend-requests") + .setAllowExpression(true) + .setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true)) + .build(); + protected static final SimpleAttributeDefinition ALWAYS_REFRESH_TOKEN = + new SimpleAttributeDefinitionBuilder("always-refresh-token", ModelType.BOOLEAN, true) + .setXmlName("always-refresh-token") + .setAllowExpression(true) + .setDefaultValue(new ModelNode(false)) + .build(); + protected static final SimpleAttributeDefinition REGISTER_NODE_AT_STARTUP = + new SimpleAttributeDefinitionBuilder("register-node-at-startup", ModelType.BOOLEAN, true) + .setXmlName("register-node-at-startup") + .setAllowExpression(true) + .setDefaultValue(new ModelNode(false)) + .build(); + protected static final SimpleAttributeDefinition REGISTER_NODE_PERIOD = + new SimpleAttributeDefinitionBuilder("register-node-period", ModelType.INT, true) + .setXmlName("register-node-period") + .setAllowExpression(true) + .setValidator(new IntRangeValidator(-1, true)) + .build(); + protected static final SimpleAttributeDefinition TOKEN_STORE = + new SimpleAttributeDefinitionBuilder("token-store", ModelType.STRING, true) + .setXmlName("token-store") + .setAllowExpression(true) + .setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true)) + .build(); + protected static final SimpleAttributeDefinition PRINCIPAL_ATTRIBUTE = + new SimpleAttributeDefinitionBuilder("principal-attribute", ModelType.STRING, true) + .setXmlName("principal-attribute") + .setAllowExpression(true) + .setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true)) + .build(); protected static final List ATTRIBUTES = new ArrayList(); @@ -150,6 +186,12 @@ public class SharedAttributeDefinitons { ATTRIBUTES.add(CORS_ALLOWED_HEADERS); ATTRIBUTES.add(CORS_ALLOWED_METHODS); ATTRIBUTES.add(EXPOSE_TOKEN); + ATTRIBUTES.add(AUTH_SERVER_URL_FOR_BACKEND_REQUESTS); + ATTRIBUTES.add(ALWAYS_REFRESH_TOKEN); + ATTRIBUTES.add(REGISTER_NODE_AT_STARTUP); + ATTRIBUTES.add(REGISTER_NODE_PERIOD); + ATTRIBUTES.add(TOKEN_STORE); + ATTRIBUTES.add(PRINCIPAL_ATTRIBUTE); } /** diff --git a/integration/as7-eap-subsystem/src/main/resources/org/keycloak/subsystem/extension/LocalDescriptions.properties b/integration/as7-eap-subsystem/src/main/resources/org/keycloak/subsystem/extension/LocalDescriptions.properties index 42435c3558..e95b6cd019 100755 --- a/integration/as7-eap-subsystem/src/main/resources/org/keycloak/subsystem/extension/LocalDescriptions.properties +++ b/integration/as7-eap-subsystem/src/main/resources/org/keycloak/subsystem/extension/LocalDescriptions.properties @@ -23,6 +23,12 @@ keycloak.realm.cors-max-age=CORS max-age header keycloak.realm.cors-allowed-headers=CORS allowed headers keycloak.realm.cors-allowed-methods=CORS allowed methods keycloak.realm.expose-token=Enable secure URL that exposes access token +keycloak.realm.auth-server-url-for-backend-requests=URL to use to make background calls to auth server +keycloak.realm.always-refresh-token=Refresh token on every single web request +keycloak.realm.register-node-at-startup=Cluster setting +keycloak.realm.register-node-period=how often to re-register node +keycloak.realm.token-store=cookie or session storage for auth session data +keycloak.realm.principal-attribute=token attribute to use to set Principal name keycloak.secure-deployment=A deployment secured by Keycloak keycloak.secure-deployment.add=Add a deployment to be secured by Keycloak @@ -49,6 +55,12 @@ keycloak.secure-deployment.cors-max-age=CORS max-age header keycloak.secure-deployment.cors-allowed-headers=CORS allowed headers keycloak.secure-deployment.cors-allowed-methods=CORS allowed methods keycloak.secure-deployment.expose-token=Enable secure URL that exposes access token +keycloak.secure-deployment.auth-server-url-for-backend-requests=URL to use to make background calls to auth server +keycloak.secure-deployment.always-refresh-token=Refresh token on every single web request +keycloak.secure-deployment.register-node-at-startup=Cluster setting +keycloak.secure-deployment.register-node-period=how often to re-register node +keycloak.secure-deployment.token-store=cookie or session storage for auth session data +keycloak.secure-deployment.principal-attribute=token attribute to use to set Principal name keycloak.secure-deployment.credential=Credential value diff --git a/integration/as7-eap-subsystem/src/main/resources/schema/keycloak_1_0.xsd b/integration/as7-eap-subsystem/src/main/resources/schema/keycloak_1_0.xsd index 3e79c4f929..6afc3f1bbb 100755 --- a/integration/as7-eap-subsystem/src/main/resources/schema/keycloak_1_0.xsd +++ b/integration/as7-eap-subsystem/src/main/resources/schema/keycloak_1_0.xsd @@ -83,6 +83,12 @@ + + + + + + diff --git a/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/SharedAttributeDefinitons.java b/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/SharedAttributeDefinitons.java index 93e5e86d6b..1b3c48f0d0 100755 --- a/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/SharedAttributeDefinitons.java +++ b/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/SharedAttributeDefinitons.java @@ -126,10 +126,47 @@ public class SharedAttributeDefinitons { .build(); protected static final SimpleAttributeDefinition EXPOSE_TOKEN = new SimpleAttributeDefinitionBuilder("expose-token", ModelType.BOOLEAN, true) - .setXmlName("expose-token") - .setAllowExpression(true) - .setDefaultValue(new ModelNode(false)) - .build(); + .setXmlName("expose-token") + .setAllowExpression(true) + .setDefaultValue(new ModelNode(false)) + .build(); + protected static final SimpleAttributeDefinition AUTH_SERVER_URL_FOR_BACKEND_REQUESTS = + new SimpleAttributeDefinitionBuilder("auth-server-url-for-backend-requests", ModelType.STRING, true) + .setXmlName("auth-server-url-for-backend-requests") + .setAllowExpression(true) + .setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true)) + .build(); + protected static final SimpleAttributeDefinition ALWAYS_REFRESH_TOKEN = + new SimpleAttributeDefinitionBuilder("always-refresh-token", ModelType.BOOLEAN, true) + .setXmlName("always-refresh-token") + .setAllowExpression(true) + .setDefaultValue(new ModelNode(false)) + .build(); + protected static final SimpleAttributeDefinition REGISTER_NODE_AT_STARTUP = + new SimpleAttributeDefinitionBuilder("register-node-at-startup", ModelType.BOOLEAN, true) + .setXmlName("register-node-at-startup") + .setAllowExpression(true) + .setDefaultValue(new ModelNode(false)) + .build(); + protected static final SimpleAttributeDefinition REGISTER_NODE_PERIOD = + new SimpleAttributeDefinitionBuilder("register-node-period", ModelType.INT, true) + .setXmlName("register-node-period") + .setAllowExpression(true) + .setValidator(new IntRangeValidator(-1, true)) + .build(); + protected static final SimpleAttributeDefinition TOKEN_STORE = + new SimpleAttributeDefinitionBuilder("token-store", ModelType.STRING, true) + .setXmlName("token-store") + .setAllowExpression(true) + .setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true)) + .build(); + protected static final SimpleAttributeDefinition PRINCIPAL_ATTRIBUTE = + new SimpleAttributeDefinitionBuilder("principal-attribute", ModelType.STRING, true) + .setXmlName("principal-attribute") + .setAllowExpression(true) + .setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true)) + .build(); + protected static final List ATTRIBUTES = new ArrayList(); @@ -150,6 +187,12 @@ public class SharedAttributeDefinitons { ATTRIBUTES.add(CORS_ALLOWED_HEADERS); ATTRIBUTES.add(CORS_ALLOWED_METHODS); ATTRIBUTES.add(EXPOSE_TOKEN); + ATTRIBUTES.add(AUTH_SERVER_URL_FOR_BACKEND_REQUESTS); + ATTRIBUTES.add(ALWAYS_REFRESH_TOKEN); + ATTRIBUTES.add(REGISTER_NODE_AT_STARTUP); + ATTRIBUTES.add(REGISTER_NODE_PERIOD); + ATTRIBUTES.add(TOKEN_STORE); + ATTRIBUTES.add(PRINCIPAL_ATTRIBUTE); } /** diff --git a/integration/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/extension/LocalDescriptions.properties b/integration/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/extension/LocalDescriptions.properties index 30a063ad38..524ae4d60c 100755 --- a/integration/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/extension/LocalDescriptions.properties +++ b/integration/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/extension/LocalDescriptions.properties @@ -23,6 +23,13 @@ keycloak.realm.cors-max-age=CORS max-age header keycloak.realm.cors-allowed-headers=CORS allowed headers keycloak.realm.cors-allowed-methods=CORS allowed methods keycloak.realm.expose-token=Enable secure URL that exposes access token +keycloak.realm.auth-server-url-for-backend-requests=URL to use to make background calls to auth server +keycloak.realm.always-refresh-token=Refresh token on every single web request +keycloak.realm.register-node-at-startup=Cluster setting +keycloak.realm.register-node-period=how often to re-register node +keycloak.realm.token-store=cookie or session storage for auth session data +keycloak.realm.principal-attribute=token attribute to use to set Principal name + keycloak.secure-deployment=A deployment secured by Keycloak keycloak.secure-deployment.add=Add a deployment to be secured by Keycloak @@ -49,6 +56,12 @@ keycloak.secure-deployment.cors-max-age=CORS max-age header keycloak.secure-deployment.cors-allowed-headers=CORS allowed headers keycloak.secure-deployment.cors-allowed-methods=CORS allowed methods keycloak.secure-deployment.expose-token=Enable secure URL that exposes access token +keycloak.secure-deployment.auth-server-url-for-backend-requests=URL to use to make background calls to auth server +keycloak.secure-deployment.always-refresh-token=Refresh token on every single web request +keycloak.secure-deployment.register-node-at-startup=Cluster setting +keycloak.secure-deployment.register-node-period=how often to re-register node +keycloak.secure-deployment.token-store=cookie or session storage for auth session data +keycloak.secure-deployment.principal-attribute=token attribute to use to set Principal name keycloak.secure-deployment.credential=Credential value diff --git a/integration/wildfly-subsystem/src/main/resources/schema/wildfly-keycloak_1_0.xsd b/integration/wildfly-subsystem/src/main/resources/schema/wildfly-keycloak_1_0.xsd index 8a9979b661..ff7c16e8d5 100755 --- a/integration/wildfly-subsystem/src/main/resources/schema/wildfly-keycloak_1_0.xsd +++ b/integration/wildfly-subsystem/src/main/resources/schema/wildfly-keycloak_1_0.xsd @@ -42,6 +42,12 @@ + + + + + + @@ -74,6 +80,12 @@ + + + + + +