diff --git a/docbook/reference/en/en-US/master.xml b/docbook/reference/en/en-US/master.xml
index 6bbd3010c9..31dcd821b7 100755
--- a/docbook/reference/en/en-US/master.xml
+++ b/docbook/reference/en/en-US/master.xml
@@ -40,6 +40,8 @@
+
+
]>
@@ -128,6 +130,8 @@ This one is short
&Clustering;
&ApplicationClustering;
&Proxy;
+ &CustomAttributes;
+ &ProtocolMappers;
&Migration;
diff --git a/docbook/reference/en/en-US/modules/Overview.xml b/docbook/reference/en/en-US/modules/Overview.xml
index 53fcd8ffd7..8a545d412a 100755
--- a/docbook/reference/en/en-US/modules/Overview.xml
+++ b/docbook/reference/en/en-US/modules/Overview.xml
@@ -94,6 +94,14 @@
Multitenancy support. You can host and manage multiple realms for multiple organizations. In the same auth server
and even within the same deployed application.
+
+ Identity brokering/chaining. You can make the Keycloak server a child IDP to another SAML 2.0 or OpenID Connect IDP.
+
+
+ Token claim, assertion, and attribute mappings. You can map user attributes, roles, and role names however you want
+ into a OIDC ID Token, Access Token, SAML attribute statements, etc. This feature allows you to basically
+ tailor however you want auth responses to look.
+
Supports JBoss AS7, EAP 6.x, Wildfly, Tomcat 7, Tomcat 8, Jetty 9.1.x, Jetty 9.2.x, Jetty 8.1.x, and Pure JavaScript applications. Plans to support Node.js, RAILS, GRAILS, and other non-Java deployments
diff --git a/docbook/reference/en/en-US/modules/custom-attributes.xml b/docbook/reference/en/en-US/modules/custom-attributes.xml
new file mode 100755
index 0000000000..338cdf20fb
--- /dev/null
+++ b/docbook/reference/en/en-US/modules/custom-attributes.xml
@@ -0,0 +1,150 @@
+
+ Custom User Attributes
+ If you have custom user data you want to store and manage in the admin console, registration page, and user account service, you can easily add
+ support for it by extending and modifying various Keycloak themes.
+
+ In admin console
+ To be able to enter custom attributes in the admin console, take the following steps
+
+
+
+ Create a new theme within the themes/admin/mytheme directory in your distribution.
+ Where mytheme is whatever you want to name your theme.
+
+
+ Create a theme.properties file in this directory that extends the main admin console
+ theme.
+
+
+
+ Copy the file themes/admin/base/resources/partials/user-attribute-entry.html into the
+ a mirror directory in your theme: themes/admin/mytheme/resources/partials/user-attribute-entry.html.
+ What you are doing here is overriding the user attribute entry page in the admin console and putting in
+ what attributes you want. This file already contains an example of entering address data. You can remove
+ this if you want and replace it with something else. Also, if you want to edit this file directly instead
+ of creating a new theme, you can.
+
+
+ In the user-attribute-entry.html file add your custom user attribute entry form item. For example
+
+
+
+
+
+
+
+]]>
+ The ng-model names the user attribute you will store in the database and must have the
+ form of user.attributes.ATTR_NAME.
+
+
+ Change the theme for the admin console. Save it, then refresh your browser, and you should
+ now see these fields in the User detail page for any user.
+
+
+
+
+
+ In registration page
+ To be able to enter custom attributes in the registration page, take the following steps
+
+
+
+ Create a new theme within the themes/login/mytheme directory in your distribution.
+ Where mytheme is whatever you want to name your theme.
+
+
+ Create a theme.properties file in this directory that extends the main admin console
+ theme.
+
+
+
+ Copy the file themes/login/base/register.ftl into the
+ a mirror directory in your theme: themes/login/mytheme/register.ftl.
+ What you are doing here is overriding the registration page and adding
+ what attributes you want. This file already contains an example of entering address data. You can remove
+ this if you want and replace it with something else. Also, if you want to edit this file directly instead
+ of creating a new theme, you can.
+
+
+ In the register.ftl file add your custom user attribute entry form item. For example
+
+
+
+
+
+
+
+
+
+]]>
+ Make sure the input field id ane name match the user attribute you want to store in the database.
+ This must have the
+ form of user.attributes.ATTR_NAME. You might also want to replace the label text
+ with a message property. This will help later if you want to internationalize your pages.
+
+
+ Change the theme for the login to your new theme. Save it, then refresh your browser, and you should
+ now see these fields in the registration.
+
+
+
+
+
+ In user account profile page
+ To be able to manage custom attributes in the user account profile page, take the following steps
+
+
+
+ Create a new theme within the themes/account/mytheme directory in your distribution.
+ Where mytheme is whatever you want to name your theme.
+
+
+ Create a theme.properties file in this directory that extends the main admin console
+ theme.
+
+
+
+ Copy the file themes/account/base/account.ftl into the
+ a mirror directory in your theme: themes/account/mytheme/account.ftl.
+ What you are doing here is overriding the profile page and adding
+ what attributes you want to manage. This file already contains an example of entering address data. You can remove
+ this if you want and replace it with something else. Also, if you want to edit this file directly instead
+ of creating a new theme, you can.
+
+
+ In the account.ftl file add your custom user attribute entry form item. For example
+
+
+
+
+
+
+
+
+]]>
+ Make sure the input field id ane name match the user attribute you want to store in the database.
+ This must have the
+ form of user.attributes.ATTR_NAME. You might also want to replace the label text
+ with a message property. This will help later if you want to internationalize your pages.
+
+
+ Change the theme for the account to your new theme. Save it, then refresh your browser, and you should
+ now see these fields in the account profile page.
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docbook/reference/en/en-US/modules/protocol-mappers.xml b/docbook/reference/en/en-US/modules/protocol-mappers.xml
new file mode 100755
index 0000000000..0c85d6203a
--- /dev/null
+++ b/docbook/reference/en/en-US/modules/protocol-mappers.xml
@@ -0,0 +1,17 @@
+
+ OIDC Token and SAML Assertion Mappings
+
+ Applications that receive ID Tokens, Access Tokens, or SAML assertions may need or want different user metadata
+ and roles. Keycloak allows you to define what exactly is transferred. You can hardcode roles, claims and custom
+ attributes. You can pull user metadata into a token or assertion. You can rename roles. Basicall you have
+ a lot of control of what exactly goes back to the client.
+
+
+ Within the admin console, if you go to an application you've registered, you'll see a "Mappers" sub-menu item.
+ This is the place where you can control how a OIDC ID Token, Access Token, and SAML login response assertions look
+ like. When you click on this you'll see some default mappers that have been set up for you. Clicking the
+ "Add Builtin" button gives you the option to add other preconfigured mappers. Clicking on "Create" allows
+ you to define your own protocol mappers. The tooltips are very helpful to learn exactly what you can do
+ to tailor your tokens and assertions. They should be enough to guide you through the process.
+
+
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-detail.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-detail.html
index 35e12021aa..c3b1f9dbdb 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-detail.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-detail.html
@@ -64,13 +64,6 @@
-
-
-
-
-
-
-
diff --git a/forms/login-api/src/main/java/org/keycloak/login/LoginFormsProvider.java b/forms/login-api/src/main/java/org/keycloak/login/LoginFormsProvider.java
index f9a9c481e5..11c26fe767 100755
--- a/forms/login-api/src/main/java/org/keycloak/login/LoginFormsProvider.java
+++ b/forms/login-api/src/main/java/org/keycloak/login/LoginFormsProvider.java
@@ -1,6 +1,7 @@
package org.keycloak.login;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
@@ -35,7 +36,7 @@ public interface LoginFormsProvider extends Provider {
public Response createErrorPage();
- public Response createOAuthGrant();
+ public Response createOAuthGrant(ClientSessionModel clientSessionModel);
public Response createCode();
diff --git a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java
index f8b85c689f..be59af04a3 100755
--- a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java
+++ b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java
@@ -24,6 +24,7 @@ import org.keycloak.login.freemarker.model.IdentityProviderBean;
import org.keycloak.login.freemarker.model.TotpBean;
import org.keycloak.login.freemarker.model.UrlBean;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
@@ -75,6 +76,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
private UserModel user;
private ClientModel client;
+ private ClientSessionModel clientSession;
private UriInfo uriInfo;
@@ -213,7 +215,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
attributes.put("register", new RegisterBean(formData));
break;
case OAUTH_GRANT:
- attributes.put("oauth", new OAuthGrantBean(accessCode, client, realmRolesRequested, resourceRolesRequested, this.accessRequestMessage));
+ attributes.put("oauth", new OAuthGrantBean(accessCode, clientSession, client, realmRolesRequested, resourceRolesRequested, this.accessRequestMessage));
break;
case CODE:
attributes.put(OAuth2Constants.CODE, new CodeBean(accessCode, messageType == MessageType.ERROR ? message : null));
@@ -265,7 +267,8 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
return createResponse(LoginFormsPages.ERROR);
}
- public Response createOAuthGrant() {
+ public Response createOAuthGrant(ClientSessionModel clientSession) {
+ this.clientSession = clientSession;
return createResponse(LoginFormsPages.OAUTH_GRANT);
}
diff --git a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/OAuthGrantBean.java b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/OAuthGrantBean.java
index 6d515a1d41..431a606826 100755
--- a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/OAuthGrantBean.java
+++ b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/OAuthGrantBean.java
@@ -21,8 +21,9 @@
*/
package org.keycloak.login.freemarker.model;
-import org.keycloak.models.ClaimMask;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RoleModel;
import javax.ws.rs.core.MultivaluedMap;
@@ -41,7 +42,7 @@ public class OAuthGrantBean {
private ClientModel client;
private List claimsRequested;
- public OAuthGrantBean(String code, ClientModel client, List realmRolesRequested, MultivaluedMap resourceRolesRequested, String accessRequestMessage) {
+ public OAuthGrantBean(String code, ClientSessionModel clientSession, ClientModel client, List realmRolesRequested, MultivaluedMap resourceRolesRequested, String accessRequestMessage) {
this.code = code;
this.client = client;
this.realmRolesRequested = realmRolesRequested;
@@ -50,36 +51,12 @@ public class OAuthGrantBean {
// todo support locale
List claims = new LinkedList();
- long mask = client.getAllowedClaimsMask();
- if (ClaimMask.hasEmail(mask)) {
- claims.add("email");
- }
- if (ClaimMask.hasUsername(mask)) {
- claims.add("username");
- }
- if (ClaimMask.hasName(mask)) {
- claims.add("name");
- }
- if (ClaimMask.hasGender(mask)) {
- claims.add("gender");
- }
- if (ClaimMask.hasAddress(mask)) {
- claims.add("address");
- }
- if (ClaimMask.hasPhone(mask)) {
- claims.add("phone");
- }
- if (ClaimMask.hasPicture(mask)) {
- claims.add("picture");
- }
- if (ClaimMask.hasProfile(mask)) {
- claims.add("profile page");
- }
- if (ClaimMask.hasLocale(mask)) {
- claims.add("locale");
- }
- if (ClaimMask.hasWebsite(mask)) {
- claims.add("website");
+ if (clientSession != null) {
+ for (ProtocolMapperModel model : client.getProtocolMappers()) {
+ if (model.isConsentRequired() && model.getProtocol().equals(clientSession.getAuthMethod()) && model.getConsentText() != null) {
+ claims.add(model.getConsentText());
+ }
+ }
}
if (claims.size() > 0) this.claimsRequested = claims;
}
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/HardcodedAttributeMapper.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/HardcodedAttributeMapper.java
index 221bcfd273..455375e3f0 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/HardcodedAttributeMapper.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/HardcodedAttributeMapper.java
@@ -24,11 +24,6 @@ public class HardcodedAttributeMapper extends AbstractSAMLProtocolMapper impleme
static {
ConfigProperty property;
- property = new ConfigProperty();
- property.setName(ProtocolMapperUtils.USER_ATTRIBUTE);
- property.setLabel(ProtocolMapperUtils.USER_MODEL_ATTRIBUTE_LABEL);
- property.setHelpText(ProtocolMapperUtils.USER_MODEL_ATTRIBUTE_HELP_TEXT);
- configProperties.add(property);
AttributeStatementHelper.setConfigProperties(configProperties);
property = new ConfigProperty();
property.setName(ATTRIBUTE_VALUE);
diff --git a/services/src/main/java/org/keycloak/protocol/ProtocolMapperUtils.java b/services/src/main/java/org/keycloak/protocol/ProtocolMapperUtils.java
index 727d74df21..b91b7542a0 100755
--- a/services/src/main/java/org/keycloak/protocol/ProtocolMapperUtils.java
+++ b/services/src/main/java/org/keycloak/protocol/ProtocolMapperUtils.java
@@ -3,6 +3,7 @@ package org.keycloak.protocol;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.UserModel;
+import org.keycloak.representations.AccessToken;
import java.lang.reflect.Method;
import java.util.List;
@@ -41,4 +42,18 @@ public class ProtocolMapperUtils {
}
return null;
}
+
+ public static String[] parseRole(String role) {
+ int scopeIndex = role.indexOf('.');
+ if (scopeIndex > -1) {
+ String appName = role.substring(0, scopeIndex);
+ role = role.substring(scopeIndex + 1);
+ String[] rtn = {appName, role};
+ return rtn;
+ } else {
+ String[] rtn = {null, role};
+ return rtn;
+
+ }
+ }
}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedClaim.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedClaim.java
index b54c919c76..ecea9e8a91 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedClaim.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedClaim.java
@@ -5,6 +5,7 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
+import org.keycloak.protocol.ProtocolMapper;
import org.keycloak.protocol.ProtocolMapperUtils;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.representations.AccessToken;
@@ -44,8 +45,13 @@ public class HardcodedClaim extends AbstractOIDCProtocolMapper implements OIDCAc
property = new ConfigProperty();
property.setName(OIDCAttributeMapperHelper.JSON_TYPE);
property.setLabel(OIDCAttributeMapperHelper.JSON_TYPE);
- property.setType(ConfigProperty.STRING_TYPE);
- property.setDefaultValue(ConfigProperty.STRING_TYPE);
+ List types = new ArrayList(3);
+ types.add("String");
+ types.add("long");
+ types.add("int");
+ types.add("boolean");
+ property.setType(ProtocolMapper.ConfigProperty.LIST_TYPE);
+ property.setDefaultValue(types);
property.setHelpText("JSON type that should be used for the value of the claim. long, int, boolean, and String are valid values.");
configProperties.add(property);
property = new ConfigProperty();
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedRole.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedRole.java
index ec0f720c03..088389fbec 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedRole.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedRole.java
@@ -4,6 +4,7 @@ import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.UserSessionModel;
+import org.keycloak.protocol.ProtocolMapperUtils;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.representations.AccessToken;
@@ -48,7 +49,7 @@ public class HardcodedRole extends AbstractOIDCProtocolMapper implements OIDCAcc
@Override
public String getDisplayType() {
- return "Add Role";
+ return "Hardcoded Role";
}
@Override
@@ -58,19 +59,18 @@ public class HardcodedRole extends AbstractOIDCProtocolMapper implements OIDCAcc
@Override
public String getHelpText() {
- return "Hardcode any role specify into the token.";
+ return "Hardcode a role into the access token.";
}
@Override
public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
UserSessionModel userSession, ClientSessionModel clientSession) {
String role = mappingModel.getConfig().get(ROLE_CONFIG);
- String appName = null;
- int scopeIndex = role.indexOf('.');
- if (scopeIndex > -1) {
- appName = role.substring(0, scopeIndex);
- role = role.substring(scopeIndex + 1);
- token.addAccess(appName).addRole(role);
+ String[] scopedRole = ProtocolMapperUtils.parseRole(role);
+ String appName = scopedRole[0];
+ String roleName = scopedRole[1];
+ if (appName != null) {
+ token.addAccess(appName).addRole(roleName);
} else {
AccessToken.Access access = token.getRealmAccess();
if (access == null) {
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAttributeMapperHelper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAttributeMapperHelper.java
index b91c233172..7635a4bc3e 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAttributeMapperHelper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAttributeMapperHelper.java
@@ -2,12 +2,15 @@ package org.keycloak.protocol.oidc.mappers;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
+import org.keycloak.protocol.ProtocolMapper;
import org.keycloak.protocol.ProtocolMapperUtils;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.IDToken;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
/**
@@ -97,4 +100,40 @@ public class OIDCAttributeMapperHelper {
public static boolean includeInAccessToken(ProtocolMapperModel mappingModel) {
return "true".equals(mappingModel.getConfig().get(INCLUDE_IN_ACCESS_TOKEN));
}
+
+ public static void addAttributeConfig(List configProperties) {
+ ProtocolMapper.ConfigProperty property;
+ property = new ProtocolMapper.ConfigProperty();
+ property.setName(TOKEN_CLAIM_NAME);
+ property.setLabel(TOKEN_CLAIM_NAME_LABEL);
+ property.setType(ProtocolMapper.ConfigProperty.STRING_TYPE);
+ property.setHelpText("Name of the claim to insert into the token. This can be a fully qualified name like 'address.street'. In this case, a nested json object will be created.");
+ configProperties.add(property);
+ property = new ProtocolMapper.ConfigProperty();
+ property.setName(JSON_TYPE);
+ property.setLabel(JSON_TYPE);
+ List types = new ArrayList(3);
+ types.add("String");
+ types.add("long");
+ types.add("int");
+ types.add("boolean");
+ property.setType(ProtocolMapper.ConfigProperty.LIST_TYPE);
+ property.setDefaultValue(types);
+ property.setHelpText("JSON type that should be used to populate the json claim in the token. long, int, boolean, and String are valid values.");
+ configProperties.add(property);
+ property = new ProtocolMapper.ConfigProperty();
+ property.setName(INCLUDE_IN_ID_TOKEN);
+ property.setLabel(INCLUDE_IN_ID_TOKEN_LABEL);
+ property.setType(ProtocolMapper.ConfigProperty.BOOLEAN_TYPE);
+ property.setDefaultValue("true");
+ property.setHelpText(INCLUDE_IN_ID_TOKEN_HELP_TEXT);
+ configProperties.add(property);
+ property = new ProtocolMapper.ConfigProperty();
+ property.setName(INCLUDE_IN_ACCESS_TOKEN);
+ property.setLabel(INCLUDE_IN_ACCESS_TOKEN_LABEL);
+ property.setType(ProtocolMapper.ConfigProperty.BOOLEAN_TYPE);
+ property.setDefaultValue("true");
+ property.setHelpText(INCLUDE_IN_ACCESS_TOKEN_HELP_TEXT);
+ configProperties.add(property);
+ }
}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/RoleNameMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/RoleNameMapper.java
index f60246caa1..1e5784cce7 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/RoleNameMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/RoleNameMapper.java
@@ -58,7 +58,7 @@ public class RoleNameMapper extends AbstractOIDCProtocolMapper implements OIDCAc
@Override
public String getDisplayType() {
- return "Role Mapper";
+ return "Role Name Mapper";
}
@Override
@@ -76,29 +76,35 @@ public class RoleNameMapper extends AbstractOIDCProtocolMapper implements OIDCAc
UserSessionModel userSession, ClientSessionModel clientSession) {
String role = mappingModel.getConfig().get(ROLE_CONFIG);
String newName = mappingModel.getConfig().get(NEW_ROLE_NAME);
- String appName = null;
- int scopeIndex = role.indexOf('.');
- if (scopeIndex > -1) {
- appName = role.substring(0, scopeIndex);
+
+ String[] scopedRole = ProtocolMapperUtils.parseRole(role);
+ String[] newScopedRole = ProtocolMapperUtils.parseRole(newName);
+ String appName = scopedRole[0];
+ String roleName = scopedRole[1];
+ if (appName != null) {
AccessToken.Access access = token.getResourceAccess(appName);
if (access == null) return token;
-
- role = role.substring(scopeIndex + 1);
- if (!access.getRoles().contains(role)) return token;
- access.getRoles().remove(role);
+ if (!access.getRoles().contains(roleName)) return token;
+ access.getRoles().remove(roleName);
} else {
AccessToken.Access access = token.getRealmAccess();
if (access == null) return token;
- access.getRoles().remove(role);
+ access.getRoles().remove(roleName);
}
- String newAppName = null;
- scopeIndex = newName.indexOf('.');
- if (scopeIndex > -1) {
- newAppName = role.substring(0, scopeIndex);
- newName = role.substring(scopeIndex + 1);
- token.addAccess(newAppName).addRole(newName);
+ String newAppName = newScopedRole[0];
+ String newRoleName = newScopedRole[1];
+ AccessToken.Access access = null;
+ if (newAppName == null) {
+ access = token.getRealmAccess();
+ if (access == null) {
+ access = new AccessToken.Access();
+ token.setRealmAccess(access);
+ }
+ } else {
+ access = token.addAccess(newAppName);
}
+ access.addRole(newRoleName);
return token;
}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserAttributeMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserAttributeMapper.java
index 906eef4f15..b38283c262 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserAttributeMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserAttributeMapper.java
@@ -33,33 +33,7 @@ public class UserAttributeMapper extends AbstractOIDCProtocolMapper implements O
property.setHelpText(ProtocolMapperUtils.USER_MODEL_ATTRIBUTE_HELP_TEXT);
property.setType(ConfigProperty.STRING_TYPE);
configProperties.add(property);
- property = new ConfigProperty();
- property.setName(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME);
- property.setLabel(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME_LABEL);
- property.setType(ConfigProperty.STRING_TYPE);
- property.setHelpText("Name of the claim to insert into the token. This can be a fully qualified name like 'address.street'. In this case, a nested json object will be created.");
- configProperties.add(property);
- property = new ConfigProperty();
- property.setName(OIDCAttributeMapperHelper.JSON_TYPE);
- property.setLabel(OIDCAttributeMapperHelper.JSON_TYPE);
- property.setType(ConfigProperty.STRING_TYPE);
- property.setDefaultValue(ConfigProperty.STRING_TYPE);
- property.setHelpText("JSON type that should be used to populate the json claim in the token. long, int, boolean, and String are valid values.");
- configProperties.add(property);
- property = new ConfigProperty();
- property.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN);
- property.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_LABEL);
- property.setType(ConfigProperty.BOOLEAN_TYPE);
- property.setDefaultValue("true");
- property.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_HELP_TEXT);
- configProperties.add(property);
- property = new ConfigProperty();
- property.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN);
- property.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_LABEL);
- property.setType(ConfigProperty.BOOLEAN_TYPE);
- property.setDefaultValue("true");
- property.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_HELP_TEXT);
- configProperties.add(property);
+ OIDCAttributeMapperHelper.addAttributeConfig(configProperties);
}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserPropertyMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserPropertyMapper.java
index a47f824789..15573ac499 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserPropertyMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserPropertyMapper.java
@@ -3,7 +3,6 @@ package org.keycloak.protocol.oidc.mappers;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
-import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.protocol.ProtocolMapperUtils;
@@ -32,33 +31,7 @@ public class UserPropertyMapper extends AbstractOIDCProtocolMapper implements OI
property.setType(ConfigProperty.STRING_TYPE);
property.setHelpText(ProtocolMapperUtils.USER_MODEL_PROPERTY_HELP_TEXT);
configProperties.add(property);
- property = new ConfigProperty();
- property.setName(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME);
- property.setLabel(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME_LABEL);
- property.setType(ConfigProperty.STRING_TYPE);
- property.setHelpText("Name of the claim to insert into the token. This can be a fully qualified name like 'address.street'. In this case, a nested json object will be created.");
- configProperties.add(property);
- property = new ConfigProperty();
- property.setName(OIDCAttributeMapperHelper.JSON_TYPE);
- property.setLabel(OIDCAttributeMapperHelper.JSON_TYPE);
- property.setType(ConfigProperty.STRING_TYPE);
- property.setDefaultValue(ConfigProperty.STRING_TYPE);
- property.setHelpText("JSON type that should be used to populate the json claim in the token. long, int, boolean, and String are valid values.");
- configProperties.add(property);
- property = new ConfigProperty();
- property.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN);
- property.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_LABEL);
- property.setType(ConfigProperty.BOOLEAN_TYPE);
- property.setDefaultValue("true");
- property.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_HELP_TEXT);
- configProperties.add(property);
- property = new ConfigProperty();
- property.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN);
- property.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_LABEL);
- property.setType(ConfigProperty.BOOLEAN_TYPE);
- property.setDefaultValue("true");
- property.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_HELP_TEXT);
- configProperties.add(property);
+ OIDCAttributeMapperHelper.addAttributeConfig(configProperties);
}
public static final String PROVIDER_ID = "oidc-usermodel-property-mapper";
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserSessionNoteMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserSessionNoteMapper.java
index 4e83120d24..45b22bf3b4 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserSessionNoteMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserSessionNoteMapper.java
@@ -33,7 +33,7 @@ public class UserSessionNoteMapper extends AbstractOIDCProtocolMapper implements
configProperties.add(property);
property = new ConfigProperty();
property.setName(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME);
- property.setLabel(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME);
+ property.setLabel(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME_LABEL);
property.setType(ConfigProperty.STRING_TYPE);
property.setHelpText("Name of the claim to insert into the token. This can be a fully qualified name like 'address.street'. In this case, a nested json object will be created.");
configProperties.add(property);
diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
index 25fce1fa35..aa27f17a94 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -391,7 +391,7 @@ public class AuthenticationManager {
.setClientSessionCode(accessCode.getCode())
.setAccessRequest(realmRoles, resourceRoles)
.setClient(client)
- .createOAuthGrant();
+ .createOAuthGrant(clientSession);
}
event.success();
diff --git a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
old mode 100644
new mode 100755
index 7fb4209ca4..d26f695dd8
--- a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
+++ b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
@@ -197,7 +197,7 @@ public class IdentityBrokerService {
.setClient(clientModel)
.setUriInfo(this.uriInfo)
.setActionUri(this.uriInfo.getRequestUri())
- .createOAuthGrant(), clientModel);
+ .createOAuthGrant(null), clientModel);
}
IdentityProvider identityProvider = getIdentityProvider(providerId);
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java
index 0091e337c6..b6f45bd294 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java
@@ -607,6 +607,7 @@ public class AccessTokenTest {
app.addProtocolMapper(UserAttributeMapper.createClaimMapper("nested phone", "phone", "home.phone", "String", true, "", true, true));
app.addProtocolMapper(HardcodedRole.create("hard-realm", "hardcoded"));
app.addProtocolMapper(HardcodedRole.create("hard-app", "app.hardcoded"));
+ app.addProtocolMapper(RoleNameMapper.create("rename-app-role", "test-app.customer-user", "realm-user"));
session.getTransaction().commit();
session.close();
}
@@ -647,6 +648,8 @@ public class AccessTokenTest {
nested = (Map)accessToken.getOtherClaims().get("home");
Assert.assertEquals("617-777-6666", nested.get("phone"));
Assert.assertTrue(accessToken.getRealmAccess().getRoles().contains("hardcoded"));
+ Assert.assertTrue(accessToken.getRealmAccess().getRoles().contains("realm-user"));
+ Assert.assertFalse(accessToken.getResourceAccess("test-app").getRoles().contains("customer-user"));
Assert.assertTrue(accessToken.getResourceAccess("app").getRoles().contains("hardcoded"));
@@ -665,6 +668,7 @@ public class AccessTokenTest {
|| model.getName().equals("hard-nested")
|| model.getName().equals("custom phone")
|| model.getName().equals("nested phone")
+ || model.getName().equals("rename-app-role")
|| model.getName().equals("hard-realm")
|| model.getName().equals("hard-app")
) {