diff --git a/forms/src/main/java/org/keycloak/forms/OAuthGrantBean.java b/forms/src/main/java/org/keycloak/forms/OAuthGrantBean.java new file mode 100644 index 0000000000..ce7bf04da8 --- /dev/null +++ b/forms/src/main/java/org/keycloak/forms/OAuthGrantBean.java @@ -0,0 +1,88 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2012, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.keycloak.forms; + +import java.util.ArrayList; +import java.util.List; + +import javax.ws.rs.core.MultivaluedMap; + +import org.keycloak.models.RoleModel; +import org.keycloak.models.UserModel; + +/** + * @author Viliam Rockai + */ +public class OAuthGrantBean { + + private MultivaluedMap resourceRolesRequested; + private List realmRolesRequested; + private UserModel client; + private String oAuthCode; + private String action; + + public String getAction() { + return action; + } + + public void setAction(String action) { + this.action = action; + } + + public List getResourceNames(){ + return new ArrayList(resourceRolesRequested.keySet()); + } + + public MultivaluedMap getResourceRolesRequested() { + return resourceRolesRequested; + } + + public void setResourceRolesRequested(MultivaluedMap resourceRolesRequested) { + this.resourceRolesRequested = resourceRolesRequested; + } + + public List getRealmRolesRequested() { + return realmRolesRequested; + } + + public void setRealmRolesRequested(List realmRolesRequested) { + this.realmRolesRequested = realmRolesRequested; + } + + public UserModel getClient() { + return client; + } + + public void setClient(UserModel client) { + this.client = client; + } + + // lowercase "o" needed for FM template to access the property + public String getoAuthCode() { + return oAuthCode; + } + + public void setoAuthCode(String oAuthCode) { + this.oAuthCode = oAuthCode; + } + +} diff --git a/forms/src/main/java/org/keycloak/service/FormServiceImpl.java b/forms/src/main/java/org/keycloak/service/FormServiceImpl.java index 27c2df1bd8..9489448ebe 100644 --- a/forms/src/main/java/org/keycloak/service/FormServiceImpl.java +++ b/forms/src/main/java/org/keycloak/service/FormServiceImpl.java @@ -34,6 +34,7 @@ import freemarker.template.TemplateException; import org.jboss.resteasy.logging.Logger; import org.keycloak.forms.ErrorBean; import org.keycloak.forms.LoginBean; +import org.keycloak.forms.OAuthGrantBean; import org.keycloak.forms.RealmBean; import org.keycloak.forms.RegisterBean; import org.keycloak.forms.SocialBean; @@ -42,7 +43,6 @@ import org.keycloak.forms.TotpBean; import org.keycloak.forms.UrlBean; import org.keycloak.forms.UserBean; import org.keycloak.services.FormService; -import org.keycloak.services.resources.flows.FormFlows; import org.keycloak.services.resources.flows.Pages; /** @@ -71,6 +71,7 @@ public class FormServiceImpl implements FormService { commandMap.put(Pages.LOGIN_TOTP, new CommandLoginTotp()); commandMap.put(Pages.LOGIN_VERIFY_EMAIL, new CommandLoginTotp()); commandMap.put(Pages.ERROR, new CommandError()); + commandMap.put(Pages.OAUTH_GRANT, new CommandOAuthGrant()); } public String getId(){ @@ -259,6 +260,20 @@ public class FormServiceImpl implements FormService { } } + private class CommandOAuthGrant implements Command { + public void exec(Map attributes, FormServiceDataBean dataBean) { + + OAuthGrantBean oauth = new OAuthGrantBean(); + oauth.setAction(dataBean.getOAuthAction()); + oauth.setResourceRolesRequested(dataBean.getOAuthResourceRolesRequested()); + oauth.setClient(dataBean.getOAuthClient()); + oauth.setoAuthCode(dataBean.getOAuthCode()); + oauth.setRealmRolesRequested(dataBean.getOAuthRealmRolesRequested()); + + attributes.put("oauth", oauth); + } + } + private interface Command { public void exec(Map attributes, FormServiceDataBean dataBean); } diff --git a/forms/src/main/resources/META-INF/resources/forms/theme/default/css/forms.css b/forms/src/main/resources/META-INF/resources/forms/theme/default/css/forms.css index f3fadb5a8c..0d6bfbf985 100644 --- a/forms/src/main/resources/META-INF/resources/forms/theme/default/css/forms.css +++ b/forms/src/main/resources/META-INF/resources/forms/theme/default/css/forms.css @@ -102,6 +102,16 @@ a.button.disabled { font-weight: normal; letter-spacing: 0.06363636363636em; } + +input[type="button"].btn-secondary, +button.btn-secondary, +input[type="submit"].btn-secondary, +a.button.btn-secondary { + background-color: #EEEEEE; + border-color: #BBBBBB; + color: #4D5258; +} + input[type="button"].disabled:hover, input[type="submit"].disabled:hover, button.disabled:hover, diff --git a/forms/src/main/resources/META-INF/resources/forms/theme/default/login-oauth-grant.ftl b/forms/src/main/resources/META-INF/resources/forms/theme/default/login-oauth-grant.ftl index c115777669..edc7ef76a2 100755 --- a/forms/src/main/resources/META-INF/resources/forms/theme/default/login-oauth-grant.ftl +++ b/forms/src/main/resources/META-INF/resources/forms/theme/default/login-oauth-grant.ftl @@ -11,20 +11,32 @@ <#elseif section = "form">
-

This application requests access to:

+

${oauth.client.loginName} requests access to:

    -
  • - View basic information about your account -
  • -
  • - View your email address -
  • + <#list oauth.realmRolesRequested as role> +
  • + ${role.description} +
  • +
+ + <#list oauth.resourceRolesRequested?keys as resourceRole> +

${resourceRole} requests access to:

+
    + <#list oauth.resourceRolesRequested[resourceRole] as role> +
  • + ${role.description} +
  • + +
+ +

Keycloak Central Login and Google will use this information in accordance with their respective terms of service and privacy policies.

-
- - -
+
+ + + +
<#elseif section = "info" > diff --git a/services/src/main/java/org/keycloak/services/FormService.java b/services/src/main/java/org/keycloak/services/FormService.java index a70e4573bf..6e5813855b 100755 --- a/services/src/main/java/org/keycloak/services/FormService.java +++ b/services/src/main/java/org/keycloak/services/FormService.java @@ -22,10 +22,12 @@ package org.keycloak.services; import java.net.URI; +import java.util.List; import javax.ws.rs.core.MultivaluedMap; import org.keycloak.models.RealmModel; +import org.keycloak.models.RoleModel; import org.keycloak.models.UserModel; import org.keycloak.services.resources.flows.FormFlows; @@ -133,5 +135,53 @@ public interface FormService { public void setErrorType(FormFlows.ErrorType errorType) { this.errorType = errorType; } + + /* OAuth Part */ + private MultivaluedMap oAuthResourceRolesRequested; + private List oAuthRealmRolesRequested; + private UserModel oAuthClient; + private String oAuthCode; + private String oAuthAction; + + public String getOAuthAction() { + return oAuthAction; + } + + public void setOAuthAction(String action) { + this.oAuthAction = action; + } + + public MultivaluedMap getOAuthResourceRolesRequested() { + return oAuthResourceRolesRequested; + } + + public void setOAuthResourceRolesRequested(MultivaluedMap resourceRolesRequested) { + this.oAuthResourceRolesRequested = resourceRolesRequested; + } + + public List getOAuthRealmRolesRequested() { + return oAuthRealmRolesRequested; + } + + public void setOAuthRealmRolesRequested(List realmRolesRequested) { + this.oAuthRealmRolesRequested = realmRolesRequested; + } + + public UserModel getOAuthClient() { + return oAuthClient; + } + + public void setOAuthClient(UserModel client) { + this.oAuthClient = client; + } + + public String getOAuthCode() { + return oAuthCode; + } + + public void setOAuthCode(String oAuthCode) { + this.oAuthCode = oAuthCode; + } + } } diff --git a/services/src/main/java/org/keycloak/services/resources/flows/FormFlows.java b/services/src/main/java/org/keycloak/services/resources/flows/FormFlows.java index 133b062354..c4fe4a6d25 100755 --- a/services/src/main/java/org/keycloak/services/resources/flows/FormFlows.java +++ b/services/src/main/java/org/keycloak/services/resources/flows/FormFlows.java @@ -23,9 +23,11 @@ package org.keycloak.services.resources.flows; import java.net.URI; import java.util.Iterator; +import java.util.List; import org.jboss.resteasy.spi.HttpRequest; import org.jboss.resteasy.spi.ResteasyUriInfo; +import org.keycloak.models.RoleModel; import org.keycloak.services.FormService; import org.keycloak.services.email.EmailSender; import org.keycloak.services.managers.AccessCodeEntry; @@ -100,9 +102,7 @@ public class FormFlows { return forwardToForm(Pages.ACCOUNT); } - private Response forwardToForm(String template) { - - FormService.FormServiceDataBean formDataBean = new FormService.FormServiceDataBean(realm, userModel, formData, error); + private Response forwardToForm(String template, FormService.FormServiceDataBean formDataBean) { formDataBean.setErrorType(errorType == null ? ErrorType.ERROR : errorType); // Getting URI needed by form processing service @@ -140,6 +140,13 @@ public class FormFlows { return Response.status(200).entity("form provider not found").build(); } + private Response forwardToForm(String template) { + + FormService.FormServiceDataBean formDataBean = new FormService.FormServiceDataBean(realm, userModel, formData, error); + return forwardToForm(template, formDataBean); + + } + public Response forwardToLogin() { return forwardToForm(Pages.LOGIN); } @@ -172,6 +179,19 @@ public class FormFlows { return forwardToForm(Pages.ERROR); } + public Response forwardToOAuthGrant(){ + + FormService.FormServiceDataBean formDataBean = new FormService.FormServiceDataBean(realm, userModel, formData, error); + + formDataBean.setOAuthRealmRolesRequested((List) request.getAttribute("realmRolesRequested")); + formDataBean.setOAuthResourceRolesRequested((MultivaluedMap) request.getAttribute("resourceRolesRequested")); + formDataBean.setOAuthClient((UserModel)request.getAttribute("client")); + formDataBean.setOAuthCode((String)request.getAttribute("code")); + formDataBean.setOAuthAction((String)request.getAttribute("action")); + + return forwardToForm(Pages.OAUTH_GRANT, formDataBean); + } + public FormFlows setAccessCode(AccessCodeEntry accessCode) { this.accessCode = accessCode; return this; diff --git a/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java b/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java index 06c0a06b07..5d01a39cf1 100755 --- a/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java +++ b/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java @@ -121,8 +121,7 @@ public class OAuthFlows { request.setAttribute("action", TokenService.processOAuthUrl(uriInfo).build(realm.getId()).toString()); request.setAttribute("code", accessCode.getCode()); - request.forward(Pages.OAUTH_GRANT); - return null; + return Flows.forms(realm, request, uriInfo).setAccessCode(accessCode).forwardToOAuthGrant(); } public Response forwardToSecurityFailure(String message) { diff --git a/services/src/main/java/org/keycloak/services/resources/flows/Pages.java b/services/src/main/java/org/keycloak/services/resources/flows/Pages.java index 110da3ad25..695ecbb7af 100644 --- a/services/src/main/java/org/keycloak/services/resources/flows/Pages.java +++ b/services/src/main/java/org/keycloak/services/resources/flows/Pages.java @@ -38,7 +38,7 @@ public class Pages { public final static String LOGIN_VERIFY_EMAIL = "/forms/login-verify-email.ftl"; - public final static String OAUTH_GRANT = "/saas/oauthGrantForm.jsp"; + public final static String OAUTH_GRANT = "/forms/login-oauth-grant.ftl"; public final static String PASSWORD = "/forms/password.ftl"; diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthGrantServlet.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthGrantServlet.java new file mode 100644 index 0000000000..c0ae537816 --- /dev/null +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthGrantServlet.java @@ -0,0 +1,112 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2012, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.keycloak.testsuite; + +import java.io.IOException; +import java.io.PrintWriter; +import java.security.PublicKey; +import java.util.Map; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.core.UriBuilder; + +import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder; +import org.keycloak.PemUtils; +import org.keycloak.RSATokenVerifier; +import org.keycloak.representations.SkeletonKeyToken; +import org.keycloak.servlet.ServletOAuthClient; + +/** + * @author Viliam Rockai + */ +public class OAuthGrantServlet extends HttpServlet { + + public static ServletOAuthClient client; + + private static String baseUrl = Constants.AUTH_SERVER_ROOT + "/rest"; + private static String realm = "test"; + + private static String realmKeyPem = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvg" + + "cwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/" + + "p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB"; + + public void init() { + client = new ServletOAuthClient(); + client.setClientId("third-party"); + client.setPassword("password"); + client.setAuthUrl(UriBuilder.fromUri(baseUrl + "/realms/" + realm + "/tokens/login").build().toString()); + client.setCodeUrl(UriBuilder.fromUri(baseUrl + "/realms/" + realm + "/tokens/access/codes").build().toString()); + client.setClient(new ResteasyClientBuilder().build()); + client.start(); + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{ + + PrintWriter pw = resp.getWriter(); + + // Error "access_denied" happens after clicking on cancel when asked for granting permission + if (req.getParameterValues("error") != null){ + pw.print("Access rights not granted."); + + // Code is sent as a parameter in case that access was granted + } else if(req.getParameterValues("code") != null) { + String token = client.getBearerToken(req); + + pw.print("Access rights granted.
Token:"+token+"
"); + + // Check whether the token itself is relevant + try { + PublicKey realmKey = PemUtils.decodePublicKey(realmKeyPem); + SkeletonKeyToken skeletonToken = RSATokenVerifier.verifyToken(token, realmKey, realm); + + // Writing app/role information on a page in format which is easy to assert in a test. + pw.print("Role:"); + for(String role: skeletonToken.getRealmAccess().getRoles()){ + pw.print(role); + } + pw.print(".
"); + + for(Map.Entry entry: skeletonToken.getResourceAccess().entrySet()){ + pw.print("App:"+entry.getKey()+";"); + for(String role: entry.getValue().getRoles()){ + pw.print(role); + } + } + pw.print(".
"); + } catch (Exception e){ + } + + pw.print(""); + + // If no code was sent or error happened, it's 1st visit to servlet and we need to ask for permissions + } else { + client.redirectRelative("", req, resp); + } + + pw.flush(); + } + +} diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OAuthGrantTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OAuthGrantTest.java new file mode 100644 index 0000000000..0513f6707d --- /dev/null +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OAuthGrantTest.java @@ -0,0 +1,124 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2012, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.keycloak.testsuite.oauth; + +import java.io.IOException; + +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.keycloak.testsuite.OAuthClient; +import org.keycloak.testsuite.OAuthGrantServlet; +import org.keycloak.testsuite.pages.LoginPage; +import org.keycloak.testsuite.pages.OAuthGrantPage; +import org.keycloak.testsuite.rule.KeycloakRule; +import org.keycloak.testsuite.rule.WebResource; +import org.keycloak.testsuite.rule.WebRule; +import org.openqa.selenium.WebDriver; + +/** + * @author Viliam Rockai + */ +public class OAuthGrantTest { + + @ClassRule + public static KeycloakRule keycloakRule = new KeycloakRule(); + + @BeforeClass + public static void before() { + keycloakRule.deployServlet("grant", "/grant", OAuthGrantServlet.class); + } + + @Rule + public WebRule webRule = new WebRule(this); + + @WebResource + protected WebDriver driver; + + @WebResource + protected OAuthClient oauth; + + @WebResource + protected LoginPage loginPage; + + @WebResource + protected OAuthGrantPage grantPage; + + private static String GRANT_APP_URL = "http://localhost:8081/grant/"; + + private static String ACCESS_GRANTED = "Access rights granted."; + private static String ACCESS_NOT_GRANTED = "Access rights not granted."; + + private static String ROLE_USER = "Have User privileges"; + private static String ROLE_CUSTOMER = "Have Customer User privileges"; + + private static String GRANT_ROLE = "user"; + private static String GRANT_APP = "test-app"; + private static String GRANT_APP_ROLE = "customer-user"; + + @Test + public void oauthGrantAcceptTest() throws IOException { + + driver.navigate().to(GRANT_APP_URL); + + Assert.assertFalse(driver.getPageSource().contains(ACCESS_GRANTED)); + Assert.assertFalse(driver.getPageSource().contains(ACCESS_NOT_GRANTED)); + + loginPage.isCurrent(); + loginPage.login("test-user@localhost", "password"); + + grantPage.assertCurrent(); + Assert.assertTrue(driver.getPageSource().contains(ROLE_USER)); + Assert.assertTrue(driver.getPageSource().contains(ROLE_CUSTOMER)); + + grantPage.accept(); + + Assert.assertTrue(driver.getPageSource().contains(ACCESS_GRANTED)); + Assert.assertFalse(driver.getPageSource().contains(ACCESS_NOT_GRANTED)); + + Assert.assertTrue(driver.getPageSource().contains("Role:"+ GRANT_ROLE +".")); + Assert.assertTrue(driver.getPageSource().contains("App:"+ GRANT_APP +";"+ GRANT_APP_ROLE +".")); + } + + @Test + public void oauthGrantCancelTest() throws IOException { + + driver.navigate().to(GRANT_APP_URL); + + Assert.assertFalse(driver.getPageSource().contains(ACCESS_GRANTED)); + Assert.assertFalse(driver.getPageSource().contains(ACCESS_NOT_GRANTED)); + + loginPage.isCurrent(); + loginPage.login("test-user@localhost", "password"); + + grantPage.assertCurrent(); + Assert.assertTrue(driver.getPageSource().contains(ROLE_USER)); + Assert.assertTrue(driver.getPageSource().contains(ROLE_CUSTOMER)); + + grantPage.cancel(); + + Assert.assertFalse(driver.getPageSource().contains(ACCESS_GRANTED)); + Assert.assertTrue(driver.getPageSource().contains(ACCESS_NOT_GRANTED)); + } +} diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/OAuthGrantPage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/OAuthGrantPage.java new file mode 100644 index 0000000000..8749a918ce --- /dev/null +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/OAuthGrantPage.java @@ -0,0 +1,55 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2012, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.keycloak.testsuite.pages; + +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +/** + * @author Stian Thorgersen + */ +public class OAuthGrantPage extends Page { + + @FindBy(css = "input[name=\"accept\"]") + private WebElement acceptButton; + @FindBy(css = "input[name=\"cancel\"]") + private WebElement cancelButton; + + + public void accept(){ + acceptButton.click(); + } + + public void cancel(){ + cancelButton.click(); + } + + @Override + public boolean isCurrent() { + return driver.getTitle().equals("OAuth Grant"); + } + + @Override + void open() { + } + +} diff --git a/testsuite/integration/src/test/resources/testrealm.json b/testsuite/integration/src/test/resources/testrealm.json index f48b969936..941c5db836 100755 --- a/testsuite/integration/src/test/resources/testrealm.json +++ b/testsuite/integration/src/test/resources/testrealm.json @@ -24,6 +24,14 @@ { "type" : "password", "value" : "password" } ] + }, + { + "username" : "third-party", + "enabled": true, + "credentials" : [ + { "type" : "password", + "value" : "password" } + ] } ], "roles": [ @@ -40,6 +48,16 @@ { "username": "test-user@localhost", "roles": ["user"] + }, + { + "username": "third-party", + "roles": ["KEYCLOAK_IDENTITY_REQUESTER"] + } + ], + "scopeMappings": [ + { + "username": "third-party", + "roles": ["user"] } ], "applications": [ @@ -53,6 +71,24 @@ "type": "password", "value": "password" } + ], + "roles": [ + { + "name": "customer-user", + "description": "Have Customer User privileges" + } + ], + "roleMappings": [ + { + "username": "test-user@localhost", + "roles": ["customer-user"] + } + ], + "scopeMappings": [ + { + "username": "third-party", + "roles": ["customer-user"] + } ] } ]