From 13ea0ee60937828c1a777385e84b3b78b40832a3 Mon Sep 17 00:00:00 2001 From: mposolda Date: Fri, 9 Aug 2013 20:04:11 +0200 Subject: [PATCH 01/10] Added support for Facebook login --- examples/as7-eap-demo/server/pom.xml | 5 + social/facebook/pom.xml | 34 ++++ .../social/facebook/FacebookProvider.java | 145 ++++++++++++++++++ .../social/facebook/FacebookUser.java | 77 ++++++++++ .../org.keycloak.social.SocialProvider | 1 + social/pom.xml | 1 + 6 files changed, 263 insertions(+) create mode 100644 social/facebook/pom.xml create mode 100644 social/facebook/src/main/java/org/keycloak/social/facebook/FacebookProvider.java create mode 100644 social/facebook/src/main/java/org/keycloak/social/facebook/FacebookUser.java create mode 100644 social/facebook/src/main/resources/META-INF/services/org.keycloak.social.SocialProvider diff --git a/examples/as7-eap-demo/server/pom.xml b/examples/as7-eap-demo/server/pom.xml index ee568447a6..5cfb75b9b3 100755 --- a/examples/as7-eap-demo/server/pom.xml +++ b/examples/as7-eap-demo/server/pom.xml @@ -45,6 +45,11 @@ keycloak-social-twitter ${project.version} + + org.keycloak + keycloak-social-facebook + ${project.version} + org.keycloak keycloak-sdk-html diff --git a/social/facebook/pom.xml b/social/facebook/pom.xml new file mode 100644 index 0000000000..a2bec96836 --- /dev/null +++ b/social/facebook/pom.xml @@ -0,0 +1,34 @@ + + + keycloak-social-parent + org.keycloak + 1.0-alpha-1 + ../pom.xml + + 4.0.0 + jar + + keycloak-social-facebook + Keycloak Social Facebook + + + + + org.keycloak + keycloak-social-core + ${project.version} + provided + + + org.jboss.resteasy + resteasy-client + provided + + + org.codehaus.jackson + jackson-core-asl + provided + + + diff --git a/social/facebook/src/main/java/org/keycloak/social/facebook/FacebookProvider.java b/social/facebook/src/main/java/org/keycloak/social/facebook/FacebookProvider.java new file mode 100644 index 0000000000..dbe42547c2 --- /dev/null +++ b/social/facebook/src/main/java/org/keycloak/social/facebook/FacebookProvider.java @@ -0,0 +1,145 @@ +package org.keycloak.social.facebook; + +import java.net.URI; +import java.util.UUID; + +import javax.ws.rs.client.Entity; +import javax.ws.rs.core.Form; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriBuilder; + +import org.jboss.resteasy.client.jaxrs.ResteasyClient; +import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder; +import org.keycloak.social.AuthCallback; +import org.keycloak.social.AuthRequest; +import org.keycloak.social.AuthRequestBuilder; +import org.keycloak.social.SocialProvider; +import org.keycloak.social.SocialProviderConfig; +import org.keycloak.social.SocialProviderException; +import org.keycloak.social.SocialUser; + +/** + * Social provider for Facebook + * + * @author Marek Posolda + */ +public class FacebookProvider implements SocialProvider { + + private static final String AUTHENTICATION_ENDPOINT_URL = "https://graph.facebook.com/oauth/authorize"; + + private static final String ACCESS_TOKEN_ENDPOINT_URL = "https://graph.facebook.com/oauth/access_token"; + + private static final String PROFILE_ENDPOINT_URL = "https://graph.facebook.com/me"; + + private static final String DEFAULT_RESPONSE_TYPE = "code"; + + private static final String DEFAULT_SCOPE = "email"; + + @Override + public String getId() { + return "facebook"; + } + + @Override + public AuthRequest getAuthUrl(SocialProviderConfig config) throws SocialProviderException { + String state = UUID.randomUUID().toString(); + + AuthRequestBuilder b = AuthRequestBuilder.create(state, AUTHENTICATION_ENDPOINT_URL).setQueryParam("client_id", config.getKey()) + .setQueryParam("response_type", DEFAULT_RESPONSE_TYPE).setQueryParam("scope", DEFAULT_SCOPE) + .setQueryParam("redirect_uri", config.getCallbackUrl()).setQueryParam("state", state); + + b.setAttribute("state", state); + + return b.build(); + } + + @Override + public String getRequestIdParamName() { + return "state"; + } + + @Override + public String getName() { + return "Facebook"; + } + + @Override + public SocialUser processCallback(SocialProviderConfig config, AuthCallback callback) throws SocialProviderException { + String code = callback.getQueryParam(DEFAULT_RESPONSE_TYPE); + + try { + if (!callback.getQueryParam("state").equals(callback.getAttribute("state"))) { + throw new SocialProviderException("Invalid state"); + } + + ResteasyClient client = new ResteasyClientBuilder() + .hostnameVerification(ResteasyClientBuilder.HostnameVerificationPolicy.ANY).build(); + + String accessToken = loadAccessToken(code, config, client); + + FacebookUser facebookUser = loadUser(accessToken, client); + + SocialUser socialUser = new SocialUser(facebookUser.getId()); + socialUser.setEmail(facebookUser.getEmail()); + socialUser.setLastName(facebookUser.getLastName()); + socialUser.setFirstName(facebookUser.getFirstName()); + + return socialUser; + } catch (SocialProviderException spe) { + throw spe; + } catch (Exception e) { + throw new SocialProviderException(e); + } + } + + protected String loadAccessToken(String code, SocialProviderConfig config, ResteasyClient client) throws SocialProviderException { + Form form = new Form(); + form.param("grant_type", "authorization_code") + .param("code", code) + .param("client_id", config.getKey()) + .param("client_secret", config.getSecret()) + .param("redirect_uri", config.getCallbackUrl()); + + Response response = client.target(ACCESS_TOKEN_ENDPOINT_URL).request().post(Entity.form(form)); + + if (response.getStatus() != 200) { + String errorTokenResponse = response.readEntity(String.class); + throw new SocialProviderException("Access token request to Facebook failed. Status: " + response.getStatus() + ", response: " + errorTokenResponse); + } + + String accessTokenResponse = response.readEntity(String.class); + return parseParameter(accessTokenResponse, "access_token"); + } + + protected FacebookUser loadUser(String accessToken, ResteasyClient client) throws SocialProviderException { + URI userDetailsUri = UriBuilder.fromUri(PROFILE_ENDPOINT_URL) + .queryParam("access_token", accessToken) + .queryParam("fields", "id,name,username,first_name,last_name,email") + .build(); + + Response response = client.target(userDetailsUri).request() + .header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON) + .get(); + if (response.getStatus() != 200) { + String errorTokenResponse = response.readEntity(String.class); + throw new SocialProviderException("Request to Facebook for obtaining user failed. Status: " + response.getStatus() + ", response: " + errorTokenResponse); + } + + return response.readEntity(FacebookUser.class); + } + + // Parses value of given parameter from input string like "my_param=abcd&another_param=xyz" + private String parseParameter(String input, String paramName) { + int start = input.indexOf(paramName + "="); + if (start != -1) { + input = input.substring(start + paramName.length() + 1); + int end = input.indexOf("&"); + return end==-1 ? input : input.substring(0, end); + } else { + throw new IllegalArgumentException("Parameter " + paramName + " not available in response " + input); + } + + } +} diff --git a/social/facebook/src/main/java/org/keycloak/social/facebook/FacebookUser.java b/social/facebook/src/main/java/org/keycloak/social/facebook/FacebookUser.java new file mode 100644 index 0000000000..8a30fc7a0d --- /dev/null +++ b/social/facebook/src/main/java/org/keycloak/social/facebook/FacebookUser.java @@ -0,0 +1,77 @@ +package org.keycloak.social.facebook; + +import org.codehaus.jackson.annotate.JsonProperty; + +/** + * Wrap info about user from Facebook + * + * @author Marek Posolda + */ +public class FacebookUser { + + @JsonProperty("id") + private String id; + + @JsonProperty("first_name") + private String firstName; + + @JsonProperty("last_name") + private String lastName; + + @JsonProperty("username") + private String username; + + @JsonProperty("name") + private String name; + + @JsonProperty("email") + private String email; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } +} diff --git a/social/facebook/src/main/resources/META-INF/services/org.keycloak.social.SocialProvider b/social/facebook/src/main/resources/META-INF/services/org.keycloak.social.SocialProvider new file mode 100644 index 0000000000..5c147a5107 --- /dev/null +++ b/social/facebook/src/main/resources/META-INF/services/org.keycloak.social.SocialProvider @@ -0,0 +1 @@ +org.keycloak.social.facebook.FacebookProvider \ No newline at end of file diff --git a/social/pom.xml b/social/pom.xml index 9efd60c0e4..2041baecf2 100755 --- a/social/pom.xml +++ b/social/pom.xml @@ -17,6 +17,7 @@ core google twitter + facebook From a4ba63b3d2064759b78ab54c68d6a7c650e1a666 Mon Sep 17 00:00:00 2001 From: Stian Thorgersen Date: Tue, 13 Aug 2013 14:03:54 +0100 Subject: [PATCH 02/10] Added instructions to configuring social providers --- social/README.md | 52 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 social/README.md diff --git a/social/README.md b/social/README.md new file mode 100644 index 0000000000..08f4dc1e4f --- /dev/null +++ b/social/README.md @@ -0,0 +1,52 @@ +Keycloak social +=============== + +This document describes how to configure social providers for Keycloak. At the moment social providers are configured globally using system properties. These can either be passed using '-D' when starting the application server or added to the standalone.xml file, for example: + + + + + + + + + + +Social provides implementations for Facebook, Google and Twitter. + + +Configure Facebook +------------------ + + + + +Configure Google +---------------- + +Open https://code.google.com/apis/console/. From the drop-down menu select Create. + +Use any name that you'd like, click Create Project, select API Access and click on Create an OAuth 2.0 client ID. + +Use any product name you'd like and leave the other fields empty, then click Next. On the next page select Web application as the application type. Click more options next> to Your site or hostname. Fill in the form with the following values: + +* Authorized Redirect URIs: http://[]/auth-server/rest/realms//social/callback + +Click on Create client ID. Use the value of Client ID as the value of the system property "keycloak.social.google.key", and the value of Client secret as the value of "keycloak.social.google.secret". + + +Configure Twitter +----------------- + +Open https://dev.twitter.com/apps. Click on Create a new application. + +Fill in name, description and website. For Callback URL use the following value: + +Callback URL: http://mbaas-stianst.rhcloud.com/ejs-identity/api/callback/516131bc-e3e4-4736-928b-cf1df8c0fe74 +Note: Twitter doesn't allow localhost as domain, use 127.0.0.1 instead! + +Agree to the rules, fill in the captcha and click on Create your Twitter application. + +Now click on Settings and tick the box Allow this application to be used to Sign in with Twitter, and click on Update this Twitter application's settings. + +Finally click on Details. Use the value of Client key as the value of the system property "keycloak.social.twitter.key", and the value of Client secret as the value of "keycloak.social.twitter.secret". From a0f0e3034890b7548396cc4cdd2572d3266c52be Mon Sep 17 00:00:00 2001 From: Stian Thorgersen Date: Tue, 13 Aug 2013 16:38:02 +0100 Subject: [PATCH 03/10] KEYCLOAK-33 Callback URL for social includes realm name --- .../resources/KeycloakApplication.java | 6 +- ...stractLoginService.java => OAuthUtil.java} | 75 ++++---- .../services/resources/RealmsResource.java | 23 +-- .../services/resources/SaasService.java | 2 +- ...SocialService.java => SocialResource.java} | 161 +++++++----------- .../services/resources/TokenService.java | 60 ++++--- social/README.md | 17 +- .../org/keycloak/social/SocialProvider.java | 5 - .../social/twitter/TwitterProvider.java | 2 +- 9 files changed, 153 insertions(+), 198 deletions(-) rename services/src/main/java/org/keycloak/services/resources/{AbstractLoginService.java => OAuthUtil.java} (63%) mode change 100755 => 100644 rename services/src/main/java/org/keycloak/services/resources/{SocialService.java => SocialResource.java} (54%) diff --git a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java index 980487c0cf..cd464248f7 100755 --- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java +++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java @@ -49,7 +49,11 @@ public class KeycloakApplication extends Application { KeycloakSessionFactory f = createSessionFactory(); this.factory = f; classes.add(KeycloakSessionCleanupFilter.class); - singletons.add(new RealmsResource(new TokenManager(), new SocialRequestManager())); + + TokenManager tokenManager = new TokenManager(); + + singletons.add(new RealmsResource(tokenManager)); + singletons.add(new SocialResource(tokenManager, new SocialRequestManager())); classes.add(SkeletonKeyContextResolver.class); classes.add(SaasService.class); } diff --git a/services/src/main/java/org/keycloak/services/resources/AbstractLoginService.java b/services/src/main/java/org/keycloak/services/resources/OAuthUtil.java old mode 100755 new mode 100644 similarity index 63% rename from services/src/main/java/org/keycloak/services/resources/AbstractLoginService.java rename to services/src/main/java/org/keycloak/services/resources/OAuthUtil.java index 6535acf4f7..857c0ce7c1 --- a/services/src/main/java/org/keycloak/services/resources/AbstractLoginService.java +++ b/services/src/main/java/org/keycloak/services/resources/OAuthUtil.java @@ -1,14 +1,13 @@ package org.keycloak.services.resources; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.HttpHeaders; +import java.net.URI; + import javax.ws.rs.core.Response; import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; import org.jboss.resteasy.logging.Logger; import org.jboss.resteasy.spi.HttpRequest; -import org.jboss.resteasy.spi.HttpResponse; import org.keycloak.services.JspRequestParameters; import org.keycloak.services.managers.AccessCodeEntry; import org.keycloak.services.managers.AuthenticationManager; @@ -18,57 +17,52 @@ import org.keycloak.services.models.RealmModel; import org.keycloak.services.models.RoleModel; import org.keycloak.services.models.UserModel; -import java.net.URI; +public class OAuthUtil { -public abstract class AbstractLoginService { - - @Context - protected UriInfo uriInfo; - @Context - protected HttpHeaders headers; - @Context - HttpRequest request; - @Context - HttpResponse response; + private static final Logger log = Logger.getLogger(OAuthUtil.class); public final static String securityFailurePath = "/saas/securityFailure.jsp"; public final static String loginFormPath = "/sdk/login.xhtml"; public final static String oauthFormPath = "/saas/oauthGrantForm.jsp"; - protected RealmModel realm; - protected TokenManager tokenManager; - protected AuthenticationManager authManager = new AuthenticationManager(); - - public AbstractLoginService(RealmModel realm, TokenManager tokenManager) { - this.realm = realm; - this.tokenManager = tokenManager; - } - - protected Response processAccessCode(String scopeParam, String state, String redirect, UserModel client, UserModel user) { + public static Response processAccessCode(RealmModel realm, TokenManager tokenManager, AuthenticationManager authManager, + HttpRequest request, UriInfo uriInfo, + String scopeParam, String state, + String redirect, + UserModel client, UserModel user) { RoleModel resourceRole = realm.getRole(RealmManager.RESOURCE_ROLE); RoleModel identityRequestRole = realm.getRole(RealmManager.IDENTITY_REQUESTER_ROLE); boolean isResource = realm.hasRole(client, resourceRole); if (!isResource && !realm.hasRole(client, identityRequestRole)) { - securityFailureForward("Login requester not allowed to request login."); + securityFailureForward(request, "Login requester not allowed to request login."); return null; } AccessCodeEntry accessCode = tokenManager.createAccessCode(scopeParam, state, redirect, realm, client, user); - getLogger().info("processAccessCode: isResource: " + isResource); - getLogger().info("processAccessCode: go to oauth page?: " - + (!isResource && (accessCode.getRealmRolesRequested().size() > 0 || accessCode.getResourceRolesRequested() - .size() > 0))); + log.info("processAccessCode: isResource: " + isResource); + log.info( + "processAccessCode: go to oauth page?: " + + (!isResource && (accessCode.getRealmRolesRequested().size() > 0 || accessCode + .getResourceRolesRequested().size() > 0))); if (!isResource && (accessCode.getRealmRolesRequested().size() > 0 || accessCode.getResourceRolesRequested().size() > 0)) { - oauthGrantPage(accessCode, client); + oauthGrantPage(realm, request, uriInfo, accessCode, client); return null; } - return redirectAccessCode(accessCode, state, redirect); + return redirectAccessCode(realm, authManager, uriInfo, accessCode, state, redirect); } - protected Response redirectAccessCode(AccessCodeEntry accessCode, String state, String redirect) { + public static void securityFailureForward(HttpRequest request, String message) { + log.error(message); + request.setAttribute(JspRequestParameters.KEYCLOAK_SECURITY_FAILURE_MESSAGE, message); + request.forward(securityFailurePath); + } + + public static Response redirectAccessCode(RealmModel realm, AuthenticationManager authManager, UriInfo uriInfo, + AccessCodeEntry accessCode, + String state, String redirect) { String code = accessCode.getCode(); UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam("code", code); - getLogger().info("redirectAccessCode: state: " + state); + log.info("redirectAccessCode: state: " + state); if (state != null) redirectUri.queryParam("state", state); Response.ResponseBuilder location = Response.status(302).location(redirectUri.build()); @@ -78,16 +72,11 @@ public abstract class AbstractLoginService { return location.build(); } - protected void securityFailureForward(String message) { - getLogger().error(message); - request.setAttribute(JspRequestParameters.KEYCLOAK_SECURITY_FAILURE_MESSAGE, message); - request.forward(securityFailurePath); - } - - protected void forwardToLoginForm(String redirect, String clientId, String scopeParam, String state) { + public static void forwardToLoginForm(RealmModel realm, HttpRequest request, UriInfo uriInfo, String redirect, + String clientId, String scopeParam, String state) { request.setAttribute(RealmModel.class.getName(), realm); request.setAttribute("KEYCLOAK_LOGIN_ACTION", TokenService.processLoginUrl(uriInfo).build(realm.getId())); - request.setAttribute("KEYCLOAK_SOCIAL_LOGIN", SocialService.redirectToProviderAuthUrl(uriInfo).build(realm.getId())); + request.setAttribute("KEYCLOAK_SOCIAL_LOGIN", SocialResource.redirectToProviderAuthUrl(uriInfo).build(realm.getId())); request.setAttribute("KEYCLOAK_REGISTRATION_PAGE", URI.create("not-implemented-yet")); // RESTEASY eats the form data, so we send via an attribute @@ -98,7 +87,8 @@ public abstract class AbstractLoginService { request.forward(loginFormPath); } - protected void oauthGrantPage(AccessCodeEntry accessCode, UserModel client) { + public static void oauthGrantPage(RealmModel realm, HttpRequest request, UriInfo uriInfo, AccessCodeEntry accessCode, + UserModel client) { request.setAttribute("realmRolesRequested", accessCode.getRealmRolesRequested()); request.setAttribute("resourceRolesRequested", accessCode.getResourceRolesRequested()); request.setAttribute("client", client); @@ -108,5 +98,4 @@ public abstract class AbstractLoginService { request.forward(oauthFormPath); } - protected abstract Logger getLogger(); } diff --git a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java index 437502595a..9f626a4f6e 100755 --- a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java +++ b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java @@ -43,11 +43,8 @@ public class RealmsResource { protected TokenManager tokenManager; - protected SocialRequestManager socialRequestManager; - - public RealmsResource(TokenManager tokenManager, SocialRequestManager socialRequestManager) { + public RealmsResource(TokenManager tokenManager) { this.tokenManager = tokenManager; - this.socialRequestManager = socialRequestManager; } public static UriBuilder realmBaseUrl(UriInfo uriInfo) { @@ -73,24 +70,6 @@ public class RealmsResource { } - @Path("{realm}/social") - public SocialService getSocialService(final @PathParam("realm") String id) { - return new Transaction(false) { - @Override - protected SocialService callImpl() { - RealmManager realmManager = new RealmManager(session); - RealmModel realm = realmManager.getRealm(id); - if (realm == null) { - logger.debug("realm not found"); - throw new NotFoundException(); - } - SocialService socialService = new SocialService(realm, tokenManager, socialRequestManager); - resourceContext.initResource(socialService); - return socialService; - } - }.call(); - } - @Path("{realm}") public PublicRealmResource getRealmResource(final @PathParam("realm") String id) { return new Transaction(false) { diff --git a/services/src/main/java/org/keycloak/services/resources/SaasService.java b/services/src/main/java/org/keycloak/services/resources/SaasService.java index 3665c53b20..7b5cb431a4 100755 --- a/services/src/main/java/org/keycloak/services/resources/SaasService.java +++ b/services/src/main/java/org/keycloak/services/resources/SaasService.java @@ -222,7 +222,7 @@ public class SaasService { URI register = contextRoot(uriInfo).path(saasRegisterPath).build(); request.setAttribute("KEYCLOAK_LOGIN_ACTION", action); request.setAttribute("KEYCLOAK_REGISTRATION_PAGE", register); - request.setAttribute("KEYCLOAK_SOCIAL_LOGIN", SocialService.redirectToProviderAuthUrl(uriInfo).build(realm.getId())); + request.setAttribute("KEYCLOAK_SOCIAL_LOGIN", SocialResource.redirectToProviderAuthUrl(uriInfo).build(realm.getId())); request.forward(loginFormPath); } diff --git a/services/src/main/java/org/keycloak/services/resources/SocialService.java b/services/src/main/java/org/keycloak/services/resources/SocialResource.java similarity index 54% rename from services/src/main/java/org/keycloak/services/resources/SocialService.java rename to services/src/main/java/org/keycloak/services/resources/SocialResource.java index d497f8ebaf..35ed9ab2f9 100644 --- a/services/src/main/java/org/keycloak/services/resources/SocialService.java +++ b/services/src/main/java/org/keycloak/services/resources/SocialResource.java @@ -1,30 +1,8 @@ -/* - * 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.services.resources; import java.net.URISyntaxException; import java.util.HashMap; import java.util.Iterator; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -32,17 +10,19 @@ import java.util.Map.Entry; import javax.imageio.spi.ServiceRegistry; import javax.ws.rs.GET; import javax.ws.rs.Path; -import javax.ws.rs.Produces; +import javax.ws.rs.PathParam; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; import org.jboss.resteasy.logging.Logger; +import org.jboss.resteasy.spi.HttpRequest; +import org.keycloak.services.managers.AuthenticationManager; +import org.keycloak.services.managers.RealmManager; import org.keycloak.services.managers.TokenManager; import org.keycloak.services.models.RealmModel; import org.keycloak.services.models.UserModel; @@ -56,39 +36,42 @@ import org.keycloak.social.SocialProviderException; import org.keycloak.social.SocialRequestManager; import org.keycloak.social.SocialUser; -/** - * @author Stian Thorgersen - */ -public class SocialService extends AbstractLoginService { +@Path("/social") +public class SocialResource { - private static final Logger logger = Logger.getLogger(SocialService.class); + protected static Logger logger = Logger.getLogger(SocialResource.class); @Context - private HttpHeaders headers; + protected UriInfo uriInfo; @Context - private UriInfo uriInfo; + protected HttpHeaders headers; + + @Context + private HttpRequest request; + + private SocialRequestManager socialRequestManager; + + private TokenManager tokenManager; + + private AuthenticationManager authManager = new AuthenticationManager(); + + public SocialResource(TokenManager tokenManager, SocialRequestManager socialRequestManager) { + this.tokenManager = tokenManager; + this.socialRequestManager = socialRequestManager; + } public static UriBuilder socialServiceBaseUrl(UriInfo uriInfo) { - UriBuilder base = uriInfo.getBaseUriBuilder().path(RealmsResource.class).path(RealmsResource.class, "getSocialService"); + UriBuilder base = uriInfo.getBaseUriBuilder().path(SocialResource.class); return base; } public static UriBuilder redirectToProviderAuthUrl(UriInfo uriInfo) { - return socialServiceBaseUrl(uriInfo).path(SocialService.class, "redirectToProviderAuth"); - + return socialServiceBaseUrl(uriInfo).path(SocialResource.class, "redirectToProviderAuth"); } public static UriBuilder callbackUrl(UriInfo uriInfo) { - return socialServiceBaseUrl(uriInfo).path(SocialService.class, "callback"); - - } - - private SocialRequestManager socialRequestManager; - - public SocialService(RealmModel realm, TokenManager tokenManager, SocialRequestManager socialRequestManager) { - super(realm, tokenManager); - this.socialRequestManager = socialRequestManager; + return socialServiceBaseUrl(uriInfo).path(SocialResource.class, "callback"); } @GET @@ -101,9 +84,11 @@ public class SocialService extends AbstractLoginService { RequestDetails requestData = getRequestDetails(queryParams); SocialProvider provider = getProvider(requestData.getProviderId()); + String realmId = requestData.getClientAttribute("realmId"); + String key = System.getProperty("keycloak.social." + requestData.getProviderId() + ".key"); String secret = System.getProperty("keycloak.social." + requestData.getProviderId() + ".secret"); - String callbackUri = callbackUrl(uriInfo).build(realm.getId()).toString(); + String callbackUri = callbackUrl(uriInfo).build().toString(); SocialProviderConfig config = new SocialProviderConfig(key, secret, callbackUri); @@ -114,12 +99,15 @@ public class SocialService extends AbstractLoginService { socialUser = provider.processCallback(config, callback); } catch (SocialProviderException e) { logger.warn("Failed to process social callback", e); - securityFailureForward("Failed to process social callback"); + OAuthUtil.securityFailureForward(request, "Failed to process social callback"); return null; } + RealmManager realmManager = new RealmManager(session); + RealmModel realm = realmManager.getRealm(realmId); + if (!realm.isEnabled()) { - securityFailureForward("Realm not enabled."); + OAuthUtil.securityFailureForward(request, "Realm not enabled."); return null; } @@ -127,11 +115,11 @@ public class SocialService extends AbstractLoginService { UserModel client = realm.getUser(clientId); if (client == null) { - securityFailureForward("Unknown login requester."); + OAuthUtil.securityFailureForward(request, "Unknown login requester."); return null; } if (!client.isEnabled()) { - securityFailureForward("Login requester not enabled."); + OAuthUtil.securityFailureForward(request, "Login requester not enabled."); return null; } @@ -149,7 +137,7 @@ public class SocialService extends AbstractLoginService { } if (!user.isEnabled()) { - securityFailureForward("Your account is not enabled."); + OAuthUtil.securityFailureForward(request, "Your account is not enabled."); return null; } @@ -157,60 +145,46 @@ public class SocialService extends AbstractLoginService { String state = requestData.getClientAttributes().get("state"); String redirectUri = requestData.getClientAttributes().get("redirectUri"); - return processAccessCode(scope, state, redirectUri, client, user); + return OAuthUtil.processAccessCode(realm, tokenManager, authManager, request, uriInfo, scope, state, + redirectUri, client, user); } }.call(); } @GET - @Path("providers") - @Produces(MediaType.APPLICATION_JSON) - public List getProviders() { - List providers = new LinkedList(); - Iterator itr = ServiceRegistry.lookupProviders(SocialProvider.class); - while (itr.hasNext()) { - providers.add(itr.next()); + @Path("{realm}/login") + public Response redirectToProviderAuth(@PathParam("realm") final String realmId, + @QueryParam("provider_id") final String providerId, @QueryParam("client_id") final String clientId, + @QueryParam("scope") final String scope, @QueryParam("state") final String state, + @QueryParam("redirect_uri") final String redirectUri) { + SocialProvider provider = getProvider(providerId); + if (provider == null) { + OAuthUtil.securityFailureForward(request, "Social provider not found"); + return null; } - return providers; - } - @GET - @Path("login") - public Response redirectToProviderAuth(@QueryParam("provider_id") final String providerId, - @QueryParam("client_id") final String clientId, @QueryParam("scope") final String scope, - @QueryParam("state") final String state, @QueryParam("redirect_uri") final String redirectUri) { - return new Transaction() { - protected Response callImpl() { - SocialProvider provider = getProvider(providerId); - if (provider == null) { - securityFailureForward("Social provider not found"); - return null; - } + String key = System.getProperty("keycloak.social." + providerId + ".key"); + String secret = System.getProperty("keycloak.social." + providerId + ".secret"); + String callbackUri = callbackUrl(uriInfo).build().toString(); - String key = System.getProperty("keycloak.social." + providerId + ".key"); - String secret = System.getProperty("keycloak.social." + providerId + ".secret"); - String callbackUri = callbackUrl(uriInfo).build(realm.getId()).toString(); + SocialProviderConfig config = new SocialProviderConfig(key, secret, callbackUri); - SocialProviderConfig config = new SocialProviderConfig(key, secret, callbackUri); + try { + AuthRequest authRequest = provider.getAuthUrl(config); - try { - AuthRequest authRequest = provider.getAuthUrl(config); + RequestDetails socialRequest = RequestDetailsBuilder.create(providerId) + .putSocialAttributes(authRequest.getAttributes()).putClientAttribute("realmId", realmId) + .putClientAttribute("clientId", clientId).putClientAttribute("scope", scope) + .putClientAttribute("state", state).putClientAttribute("redirectUri", redirectUri).build(); - RequestDetails socialRequest = RequestDetailsBuilder.create(providerId) - .putSocialAttributes(authRequest.getAttributes()).putClientAttribute("clientId", clientId) - .putClientAttribute("scope", scope).putClientAttribute("state", state) - .putClientAttribute("redirectUri", redirectUri).build(); + socialRequestManager.addRequest(authRequest.getId(), socialRequest); - socialRequestManager.addRequest(authRequest.getId(), socialRequest); - - return Response.status(Status.FOUND).location(authRequest.getAuthUri()).build(); - } catch (Throwable t) { - logger.error("Failed to redirect to social auth", t); - securityFailureForward("Failed to redirect to social auth"); - return null; - } - } - }.call(); + return Response.status(Status.FOUND).location(authRequest.getAuthUri()).build(); + } catch (Throwable t) { + logger.error("Failed to redirect to social auth", t); + OAuthUtil.securityFailureForward(request, "Failed to redirect to social auth"); + return null; + } } private RequestDetails getRequestDetails(Map queryParams) { @@ -251,9 +225,4 @@ public class SocialService extends AbstractLoginService { return queryParams; } - @Override - protected Logger getLogger() { - return logger; - } - } diff --git a/services/src/main/java/org/keycloak/services/resources/TokenService.java b/services/src/main/java/org/keycloak/services/resources/TokenService.java index 4f295fefca..8d749a059a 100755 --- a/services/src/main/java/org/keycloak/services/resources/TokenService.java +++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java @@ -6,6 +6,8 @@ import org.jboss.resteasy.jose.jws.JWSInput; import org.jboss.resteasy.jose.jws.crypto.RSAProvider; import org.jboss.resteasy.jwt.JsonSerialization; import org.jboss.resteasy.logging.Logger; +import org.jboss.resteasy.spi.HttpRequest; +import org.jboss.resteasy.spi.HttpResponse; import org.keycloak.representations.AccessTokenResponse; import org.keycloak.representations.SkeletonKeyToken; import org.keycloak.services.managers.AccessCodeEntry; @@ -25,6 +27,7 @@ import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; @@ -40,20 +43,32 @@ import java.util.Map; * @author Bill Burke * @version $Revision: 1 $ */ -public class TokenService extends AbstractLoginService { - +public class TokenService { protected static final Logger logger = Logger.getLogger(TokenService.class); + protected RealmModel realm; + protected TokenManager tokenManager; + protected AuthenticationManager authManager = new AuthenticationManager(); + @Context protected Providers providers; @Context protected SecurityContext securityContext; + @Context + protected UriInfo uriInfo; + @Context + protected HttpHeaders headers; + @Context + HttpRequest request; + @Context + HttpResponse response; private ResourceAdminManager resourceAdminManager = new ResourceAdminManager(); public TokenService(RealmModel realm, TokenManager tokenManager) { - super(realm, tokenManager); + this.realm = realm; + this.tokenManager = tokenManager; } public static UriBuilder tokenServiceBaseUrl(UriInfo uriInfo) { @@ -168,16 +183,16 @@ public class TokenService extends AbstractLoginService { String redirect = formData.getFirst("redirect_uri"); if (!realm.isEnabled()) { - securityFailureForward("Realm not enabled."); + OAuthUtil.securityFailureForward(request, "Realm not enabled."); return null; } UserModel client = realm.getUser(clientId); if (client == null) { - securityFailureForward("Unknown login requester."); + OAuthUtil.securityFailureForward(request, "Unknown login requester."); return null; } if (!client.isEnabled()) { - securityFailureForward("Login requester not enabled."); + OAuthUtil.securityFailureForward(request, "Login requester not enabled."); return null; } String username = formData.getFirst("username"); @@ -185,11 +200,11 @@ public class TokenService extends AbstractLoginService { if (user == null) { logger.error("Incorrect user name."); request.setAttribute("KEYCLOAK_LOGIN_ERROR_MESSAGE", "Incorrect user name."); - forwardToLoginForm(redirect, clientId, scopeParam, state); + OAuthUtil.forwardToLoginForm(realm, request, uriInfo, redirect, clientId, scopeParam, state); return null; } if (!user.isEnabled()) { - securityFailureForward("Your account is not enabled."); + OAuthUtil.securityFailureForward(request, "Your account is not enabled."); return null; } boolean authenticated = authManager.authenticateForm(realm, user, formData); @@ -197,11 +212,12 @@ public class TokenService extends AbstractLoginService { logger.error("Authentication failed"); request.setAttribute("username", username); request.setAttribute("KEYCLOAK_LOGIN_ERROR_MESSAGE", "Invalid credentials."); - forwardToLoginForm(redirect, clientId, scopeParam, state); + OAuthUtil.forwardToLoginForm(realm, request, uriInfo, redirect, clientId, scopeParam, state); return null; } - return processAccessCode(scopeParam, state, redirect, client, user); + return OAuthUtil.processAccessCode(realm, tokenManager, authManager, request, uriInfo, scopeParam, state, + redirect, client, user); } }.call(); } @@ -341,18 +357,18 @@ public class TokenService extends AbstractLoginService { return new Transaction() { protected Response callImpl() { if (!realm.isEnabled()) { - securityFailureForward("Realm not enabled"); + OAuthUtil.securityFailureForward(request, "Realm not enabled"); return null; } UserModel client = realm.getUser(clientId); if (client == null) { - securityFailureForward("Unknown login requester."); + OAuthUtil.securityFailureForward(request, "Unknown login requester."); transaction.rollback(); return null; } if (!client.isEnabled()) { - securityFailureForward("Login requester not enabled."); + OAuthUtil.securityFailureForward(request, "Login requester not enabled."); transaction.rollback(); session.close(); return null; @@ -362,7 +378,7 @@ public class TokenService extends AbstractLoginService { RoleModel identityRequestRole = realm.getRole(RealmManager.IDENTITY_REQUESTER_ROLE); boolean isResource = realm.hasRole(client, resourceRole); if (!isResource && !realm.hasRole(client, identityRequestRole)) { - securityFailureForward("Login requester not allowed to request login."); + OAuthUtil.securityFailureForward(request, "Login requester not allowed to request login."); transaction.rollback(); session.close(); return null; @@ -371,10 +387,11 @@ public class TokenService extends AbstractLoginService { UserModel user = authManager.authenticateIdentityCookie(realm, uriInfo, headers); if (user != null) { logger.info(user.getLoginName() + " already logged in."); - return processAccessCode(scopeParam, state, redirect, client, user); + return OAuthUtil.processAccessCode(realm, tokenManager, authManager, request, uriInfo, scopeParam, state, + redirect, client, user); } - forwardToLoginForm(redirect, clientId, scopeParam, state); + OAuthUtil.forwardToLoginForm(realm, request, uriInfo, redirect, clientId, scopeParam, state); return null; } }.call(); @@ -415,14 +432,14 @@ public class TokenService extends AbstractLoginService { logger.debug("Failed to verify signature", ignored); } if (!verifiedCode) { - securityFailureForward("Illegal access code."); + OAuthUtil.securityFailureForward(request, "Illegal access code."); session.close(); return null; } String key = input.readContent(String.class); AccessCodeEntry accessCodeEntry = tokenManager.getAccessCode(key); if (accessCodeEntry == null) { - securityFailureForward("Unknown access code."); + OAuthUtil.securityFailureForward(request, "Unknown access code."); session.close(); return null; } @@ -434,7 +451,7 @@ public class TokenService extends AbstractLoginService { return redirectAccessDenied(redirect, state); } - return redirectAccessCode(accessCodeEntry, state, redirect); + return OAuthUtil.redirectAccessCode(realm, authManager, uriInfo, accessCodeEntry, state, redirect); } }.call(); } @@ -446,9 +463,4 @@ public class TokenService extends AbstractLoginService { return location.build(); } - @Override - protected Logger getLogger() { - return logger; - } - } diff --git a/social/README.md b/social/README.md index 08f4dc1e4f..90df3c281d 100644 --- a/social/README.md +++ b/social/README.md @@ -18,7 +18,17 @@ Social provides implementations for Facebook, Google and Twitter. Configure Facebook ------------------ +Open https://developers.facebook.com/apps. Click on Create New App +Use any app name that you'd like, click Continue + +Select Disabled for Sandbox Mode + +Under Select how your app integrates with Facebook select Website with Facebook login. Fill in the form with the following values: + +* Site URL: http://[]/auth-server/rest/social/callback + +Click on Save changes. Use the value of App ID as the value of the system property "keycloak.social.facebook.key", and the value of App Secret as the value of "keycloak.social.facebook.secret". Configure Google @@ -30,7 +40,7 @@ Use any name that you'd like, click Create Project, select API Access and click Use any product name you'd like and leave the other fields empty, then click Next. On the next page select Web application as the application type. Click more options next> to Your site or hostname. Fill in the form with the following values: -* Authorized Redirect URIs: http://[]/auth-server/rest/realms//social/callback +* Authorized Redirect URIs: http://[]/auth-server/rest/social/callback Click on Create client ID. Use the value of Client ID as the value of the system property "keycloak.social.google.key", and the value of Client secret as the value of "keycloak.social.google.secret". @@ -40,10 +50,7 @@ Configure Twitter Open https://dev.twitter.com/apps. Click on Create a new application. -Fill in name, description and website. For Callback URL use the following value: - -Callback URL: http://mbaas-stianst.rhcloud.com/ejs-identity/api/callback/516131bc-e3e4-4736-928b-cf1df8c0fe74 -Note: Twitter doesn't allow localhost as domain, use 127.0.0.1 instead! +Fill in name, description and website. Leave Callback URL empty! Agree to the rules, fill in the captcha and click on Create your Twitter application. diff --git a/social/core/src/main/java/org/keycloak/social/SocialProvider.java b/social/core/src/main/java/org/keycloak/social/SocialProvider.java index 81d9216a68..70da76de24 100644 --- a/social/core/src/main/java/org/keycloak/social/SocialProvider.java +++ b/social/core/src/main/java/org/keycloak/social/SocialProvider.java @@ -21,25 +21,20 @@ */ package org.keycloak.social; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.XmlTransient; /** * @author Stian Thorgersen */ -@XmlRootElement public interface SocialProvider { String getId(); - @XmlTransient AuthRequest getAuthUrl(SocialProviderConfig config) throws SocialProviderException; String getRequestIdParamName(); String getName(); - @XmlTransient SocialUser processCallback(SocialProviderConfig config, AuthCallback callback) throws SocialProviderException; } diff --git a/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterProvider.java b/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterProvider.java index ee5175b529..d014e81137 100644 --- a/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterProvider.java +++ b/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterProvider.java @@ -49,7 +49,7 @@ public class TwitterProvider implements SocialProvider { Twitter twitter = new TwitterFactory().getInstance(); twitter.setOAuthConsumer(request.getKey(), request.getSecret()); - RequestToken requestToken = twitter.getOAuthRequestToken(); + RequestToken requestToken = twitter.getOAuthRequestToken(request.getCallbackUrl()); return AuthRequestBuilder.create(requestToken.getToken(), requestToken.getAuthenticationURL()) .setAttribute("token", requestToken.getToken()).setAttribute("tokenSecret", requestToken.getTokenSecret()) From 8876484a873db6900d7a91aa26b417702a048009 Mon Sep 17 00:00:00 2001 From: Stian Thorgersen Date: Tue, 13 Aug 2013 17:09:32 +0100 Subject: [PATCH 04/10] Fixed style of Google login --- .../META-INF/resources/sdk/theme/default/styles.css | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/sdk-html/src/main/resources/META-INF/resources/sdk/theme/default/styles.css b/sdk-html/src/main/resources/META-INF/resources/sdk/theme/default/styles.css index 83e7c2d12d..161be89f23 100644 --- a/sdk-html/src/main/resources/META-INF/resources/sdk/theme/default/styles.css +++ b/sdk-html/src/main/resources/META-INF/resources/sdk/theme/default/styles.css @@ -3,4 +3,11 @@ @IMPORT url("css/forms.css"); @IMPORT url("css/zocial/zocial.css"); @IMPORT url("css/login-register.css"); -@IMPORT url("http://fonts.googleapis.com/css?family=Open+Sans:400,300,300italic,400italic,600,600italic,700,700italic,800,800italic"); \ No newline at end of file +@IMPORT url("http://fonts.googleapis.com/css?family=Open+Sans:400,300,300italic,400italic,600,600italic,700,700italic,800,800italic"); + +.zocial.google { + background-color: #dd4b39 !important; +} +.zocial.google:before { + content: "+" !important; +} \ No newline at end of file From 0ffe1cb3545a8e0ebd15c7d05324e4bf4d1d4faa Mon Sep 17 00:00:00 2001 From: Stian Thorgersen Date: Wed, 14 Aug 2013 12:30:16 +0100 Subject: [PATCH 05/10] Renamed sdk-html for forms and added registration form --- examples/as7-eap-demo/server/pom.xml | 2 +- .../main/webapp/saas/admin/partials/menu.html | 4 +- .../src/main/webapp/saas/saas-register.jsp | 88 ------------ {sdk-html => forms}/pom.xml | 4 +- .../main/java/org/keycloak/sdk/FormsBean.java | 70 ++++++++-- .../META-INF/resources/faces-config.xml | 0 .../META-INF/resources/sdk/login.xhtml | 2 +- .../META-INF/resources/sdk/register.xhtml | 2 +- .../resources/sdk/theme/default/css/base.css | 0 .../resources/sdk/theme/default/css/forms.css | 0 .../theme/default/css/img/btn-social-fb.svg | 0 .../css/img/customer-login-screen-bg.jpg | Bin .../css/img/customer-login-screen-bg.svg | 0 .../css/img/feedback-error-arrow-down.png | Bin .../css/img/feedback-error-arrow-down.svg | 0 .../default/css/img/feedback-error-sign.png | Bin .../default/css/img/feedback-error-sign.svg | 0 .../css/img/login-register-separator.svg | 0 .../css/img/login-register-separators.png | Bin .../img/login-register-social-separators.png | Bin .../img/login-register-social-separators.svg | 0 .../default/css/img/register-login-bg.png | Bin .../sdk/theme/default/css/login-register.css | 0 .../resources/sdk/theme/default/css/reset.css | 0 .../css/zocial/zocial-regular-webfont.eot | Bin .../css/zocial/zocial-regular-webfont.svg | 0 .../css/zocial/zocial-regular-webfont.ttf | Bin .../css/zocial/zocial-regular-webfont.woff | Bin .../sdk/theme/default/css/zocial/zocial.css | 0 .../sdk/theme/default/css/zocial/zocial.less | 0 .../default/img/customer-login-screen-bg2.jpg | Bin .../default/img/login-screen-background.jpg | Bin .../sdk/theme/default/img/red-hat-logo.png | Bin .../resources/sdk/theme/default/login.xhtml | 19 +-- .../sdk/theme/default/register.xhtml | 23 ++-- .../resources/sdk/theme/default/styles.css | 0 .../sdk/theme/default/template.xhtml | 37 ++--- .../main/resources/META-INF/web-fragment.xml | 0 pom.xml | 2 +- .../services/resources/OAuthUtil.java | 17 ++- .../keycloak/services/resources/Pages.java | 8 ++ .../services/resources/SaasService.java | 129 +++++++++++++----- .../services/resources/TokenService.java | 6 +- .../org/keycloak/services/resources/Urls.java | 45 ++++++ 44 files changed, 277 insertions(+), 181 deletions(-) delete mode 100755 examples/as7-eap-demo/server/src/main/webapp/saas/saas-register.jsp rename {sdk-html => forms}/pom.xml (90%) rename sdk-html/src/main/java/org/keycloak/sdk/LoginBean.java => forms/src/main/java/org/keycloak/sdk/FormsBean.java (79%) rename {sdk-html => forms}/src/main/resources/META-INF/resources/faces-config.xml (100%) rename {sdk-html => forms}/src/main/resources/META-INF/resources/sdk/login.xhtml (85%) rename {sdk-html => forms}/src/main/resources/META-INF/resources/sdk/register.xhtml (83%) rename {sdk-html => forms}/src/main/resources/META-INF/resources/sdk/theme/default/css/base.css (100%) rename {sdk-html => forms}/src/main/resources/META-INF/resources/sdk/theme/default/css/forms.css (100%) rename {sdk-html => forms}/src/main/resources/META-INF/resources/sdk/theme/default/css/img/btn-social-fb.svg (100%) rename {sdk-html => forms}/src/main/resources/META-INF/resources/sdk/theme/default/css/img/customer-login-screen-bg.jpg (100%) rename {sdk-html => forms}/src/main/resources/META-INF/resources/sdk/theme/default/css/img/customer-login-screen-bg.svg (100%) rename {sdk-html => forms}/src/main/resources/META-INF/resources/sdk/theme/default/css/img/feedback-error-arrow-down.png (100%) rename {sdk-html => forms}/src/main/resources/META-INF/resources/sdk/theme/default/css/img/feedback-error-arrow-down.svg (100%) rename {sdk-html => forms}/src/main/resources/META-INF/resources/sdk/theme/default/css/img/feedback-error-sign.png (100%) rename {sdk-html => forms}/src/main/resources/META-INF/resources/sdk/theme/default/css/img/feedback-error-sign.svg (100%) rename {sdk-html => forms}/src/main/resources/META-INF/resources/sdk/theme/default/css/img/login-register-separator.svg (100%) rename {sdk-html => forms}/src/main/resources/META-INF/resources/sdk/theme/default/css/img/login-register-separators.png (100%) rename {sdk-html => forms}/src/main/resources/META-INF/resources/sdk/theme/default/css/img/login-register-social-separators.png (100%) rename {sdk-html => forms}/src/main/resources/META-INF/resources/sdk/theme/default/css/img/login-register-social-separators.svg (100%) rename {sdk-html => forms}/src/main/resources/META-INF/resources/sdk/theme/default/css/img/register-login-bg.png (100%) rename {sdk-html => forms}/src/main/resources/META-INF/resources/sdk/theme/default/css/login-register.css (100%) rename {sdk-html => forms}/src/main/resources/META-INF/resources/sdk/theme/default/css/reset.css (100%) rename {sdk-html => forms}/src/main/resources/META-INF/resources/sdk/theme/default/css/zocial/zocial-regular-webfont.eot (100%) rename {sdk-html => forms}/src/main/resources/META-INF/resources/sdk/theme/default/css/zocial/zocial-regular-webfont.svg (100%) rename {sdk-html => forms}/src/main/resources/META-INF/resources/sdk/theme/default/css/zocial/zocial-regular-webfont.ttf (100%) rename {sdk-html => forms}/src/main/resources/META-INF/resources/sdk/theme/default/css/zocial/zocial-regular-webfont.woff (100%) rename {sdk-html => forms}/src/main/resources/META-INF/resources/sdk/theme/default/css/zocial/zocial.css (100%) rename {sdk-html => forms}/src/main/resources/META-INF/resources/sdk/theme/default/css/zocial/zocial.less (100%) rename {sdk-html => forms}/src/main/resources/META-INF/resources/sdk/theme/default/img/customer-login-screen-bg2.jpg (100%) rename {sdk-html => forms}/src/main/resources/META-INF/resources/sdk/theme/default/img/login-screen-background.jpg (100%) rename {sdk-html => forms}/src/main/resources/META-INF/resources/sdk/theme/default/img/red-hat-logo.png (100%) rename {sdk-html => forms}/src/main/resources/META-INF/resources/sdk/theme/default/login.xhtml (54%) rename {sdk-html => forms}/src/main/resources/META-INF/resources/sdk/theme/default/register.xhtml (56%) rename {sdk-html => forms}/src/main/resources/META-INF/resources/sdk/theme/default/styles.css (100%) rename {sdk-html => forms}/src/main/resources/META-INF/resources/sdk/theme/default/template.xhtml (58%) rename {sdk-html => forms}/src/main/resources/META-INF/web-fragment.xml (100%) create mode 100644 services/src/main/java/org/keycloak/services/resources/Pages.java create mode 100644 services/src/main/java/org/keycloak/services/resources/Urls.java diff --git a/examples/as7-eap-demo/server/pom.xml b/examples/as7-eap-demo/server/pom.xml index 5cfb75b9b3..a0b7c26e2f 100755 --- a/examples/as7-eap-demo/server/pom.xml +++ b/examples/as7-eap-demo/server/pom.xml @@ -52,7 +52,7 @@ org.keycloak - keycloak-sdk-html + keycloak-forms ${project.version} diff --git a/examples/as7-eap-demo/server/src/main/webapp/saas/admin/partials/menu.html b/examples/as7-eap-demo/server/src/main/webapp/saas/admin/partials/menu.html index e10cd0cd37..21c473373a 100755 --- a/examples/as7-eap-demo/server/src/main/webapp/saas/admin/partials/menu.html +++ b/examples/as7-eap-demo/server/src/main/webapp/saas/admin/partials/menu.html @@ -12,8 +12,8 @@