Details
diff --git a/examples/as7-eap-demo/server/src/main/resources/META-INF/testrealm.json b/examples/as7-eap-demo/server/src/main/resources/META-INF/testrealm.json
index 2fa8a91..2a711d0 100755
--- a/examples/as7-eap-demo/server/src/main/resources/META-INF/testrealm.json
+++ b/examples/as7-eap-demo/server/src/main/resources/META-INF/testrealm.json
@@ -5,6 +5,7 @@
"accessCodeLifespan": 10,
"sslNotRequired": true,
"cookieLoginAllowed": true,
+ "registrationAllowed": true,
"privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"requiredCredentials": [ "password" ],
diff --git a/services/src/main/java/org/keycloak/services/managers/RealmManager.java b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
index 524fb88..1586236 100755
--- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
@@ -103,6 +103,7 @@ public class RealmManager {
newRealm.setAccessCodeLifespan(rep.getAccessCodeLifespan());
newRealm.setSslNotRequired(rep.isSslNotRequired());
newRealm.setCookieLoginAllowed(rep.isCookieLoginAllowed());
+ newRealm.setRegistrationAllowed(rep.isRegistrationAllowed());
if (rep.getPrivateKey() == null || rep.getPublicKey() == null) {
generateRealmKeys(newRealm);
} else {
diff --git a/services/src/main/java/org/keycloak/services/models/RequiredCredentialModel.java b/services/src/main/java/org/keycloak/services/models/RequiredCredentialModel.java
index a72bcba..de99a7c 100755
--- a/services/src/main/java/org/keycloak/services/models/RequiredCredentialModel.java
+++ b/services/src/main/java/org/keycloak/services/models/RequiredCredentialModel.java
@@ -62,19 +62,19 @@ public class RequiredCredentialModel {
PASSWORD.setType(CredentialRepresentation.PASSWORD);
PASSWORD.setInput(true);
PASSWORD.setSecret(true);
- PASSWORD.setFormLabel("Password");
+ PASSWORD.setFormLabel("password");
map.put(PASSWORD.getType(), PASSWORD);
TOTP = new RequiredCredentialModel();
TOTP.setType(CredentialRepresentation.TOTP);
TOTP.setInput(true);
TOTP.setSecret(false);
- TOTP.setFormLabel("Authenticator Code");
+ TOTP.setFormLabel("authenticatorCode");
map.put(TOTP.getType(), TOTP);
CLIENT_CERT = new RequiredCredentialModel();
CLIENT_CERT.setType(CredentialRepresentation.CLIENT_CERT);
CLIENT_CERT.setInput(false);
CLIENT_CERT.setSecret(false);
- CLIENT_CERT.setFormLabel("Client Certificate");
+ CLIENT_CERT.setFormLabel("clientCertificate");
map.put(CLIENT_CERT.getType(), CLIENT_CERT);
BUILT_IN = Collections.unmodifiableMap(map);
}
diff --git a/services/src/main/java/org/keycloak/services/resources/flows/Flows.java b/services/src/main/java/org/keycloak/services/resources/flows/Flows.java
new file mode 100644
index 0000000..67f6a99
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/flows/Flows.java
@@ -0,0 +1,28 @@
+package org.keycloak.services.resources.flows;
+
+import javax.ws.rs.core.UriInfo;
+
+import org.jboss.resteasy.spi.HttpRequest;
+import org.keycloak.services.managers.AuthenticationManager;
+import org.keycloak.services.managers.TokenManager;
+import org.keycloak.services.models.RealmModel;
+
+public class Flows {
+
+ private Flows() {
+ }
+
+ public static PageFlows pages(HttpRequest request) {
+ return new PageFlows(request);
+ }
+
+ public static FormFlows forms(RealmModel realm, HttpRequest request) {
+ return new FormFlows(realm, request);
+ }
+
+ public static OAuthFlows oauth(RealmModel realm, HttpRequest request, UriInfo uriInfo, AuthenticationManager authManager,
+ TokenManager tokenManager) {
+ return new OAuthFlows(realm, request, uriInfo, authManager, tokenManager);
+ }
+
+}
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
new file mode 100644
index 0000000..4c65dbb
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/flows/FormFlows.java
@@ -0,0 +1,61 @@
+package org.keycloak.services.resources.flows;
+
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+
+import org.jboss.resteasy.spi.HttpRequest;
+import org.keycloak.services.models.RealmModel;
+import org.picketlink.idm.model.sample.Realm;
+
+public class FormFlows {
+
+ public static final String REALM = Realm.class.getName();
+ public static final String ERROR_MESSAGE = "KEYCLOAK_FORMS_ERROR_MESSAGE";
+ public static final String DATA = "KEYCLOAK_FORMS_DATA";
+
+ private MultivaluedMap<String, String> formData;
+ private String error;
+
+ private RealmModel realm;
+
+ private HttpRequest request;
+
+ FormFlows(RealmModel realm, HttpRequest request) {
+ this.realm = realm;
+ this.request = request;
+ }
+
+ public FormFlows setFormData(MultivaluedMap<String, String> formData) {
+ this.formData = formData;
+ return this;
+ }
+
+ public FormFlows setError(String error) {
+ this.error = error;
+ return this;
+ }
+
+ public Response forwardToLogin() {
+ return forwardToForm(Pages.LOGIN);
+ }
+
+ public Response forwardToRegistration() {
+ return forwardToForm(Pages.REGISTER);
+ }
+
+ private Response forwardToForm(String form) {
+ request.setAttribute(REALM, realm);
+
+ if (error != null) {
+ request.setAttribute(ERROR_MESSAGE, error);
+ }
+
+ if (formData != null) {
+ request.setAttribute(DATA, formData);
+ }
+
+ request.forward(form);
+ return null;
+ }
+
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/flows/PageFlows.java b/services/src/main/java/org/keycloak/services/resources/flows/PageFlows.java
new file mode 100644
index 0000000..b63b232
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/flows/PageFlows.java
@@ -0,0 +1,28 @@
+package org.keycloak.services.resources.flows;
+
+import javax.ws.rs.core.Response;
+
+import org.jboss.resteasy.logging.Logger;
+import org.jboss.resteasy.spi.HttpRequest;
+import org.keycloak.services.JspRequestParameters;
+
+public class PageFlows {
+
+ private static final Logger log = Logger.getLogger(PageFlows.class);
+
+ private HttpRequest request;
+
+ PageFlows(HttpRequest request) {
+ this.request = request;
+ }
+
+ public Response forwardToSecurityFailure(String message) {
+ log.error(message);
+
+ request.setAttribute(JspRequestParameters.KEYCLOAK_SECURITY_FAILURE_MESSAGE, message);
+
+ request.forward(Pages.SECURITY_FAILURE);
+ return null;
+ }
+
+}
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
new file mode 100644
index 0000000..bb41513
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/flows/Pages.java
@@ -0,0 +1,13 @@
+package org.keycloak.services.resources.flows;
+
+public class Pages {
+
+ public final static String LOGIN = "/sdk/login.xhtml";
+
+ public final static String OAUTH_GRANT = "/saas/oauthGrantForm.jsp";
+
+ public final static String REGISTER = "/sdk/register.xhtml";
+
+ public final static String SECURITY_FAILURE = "/saas/securityFailure.jsp";
+
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/flows/Urls.java b/services/src/main/java/org/keycloak/services/resources/flows/Urls.java
new file mode 100644
index 0000000..e20f974
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/flows/Urls.java
@@ -0,0 +1,71 @@
+package org.keycloak.services.resources.flows;
+
+import java.net.URI;
+
+import javax.ws.rs.core.UriBuilder;
+
+import org.keycloak.services.resources.RealmsResource;
+import org.keycloak.services.resources.SaasService;
+import org.keycloak.services.resources.SocialResource;
+import org.keycloak.services.resources.TokenService;
+
+public class Urls {
+
+ private static UriBuilder realmBase(URI baseUri) {
+ return UriBuilder.fromUri(baseUri).path(RealmsResource.class);
+ }
+
+ private static UriBuilder tokenBase(URI baseUri) {
+ return realmBase(baseUri).path(RealmsResource.class, "getTokenService");
+ }
+
+ public static URI realmLoginAction(URI baseUri, String realmId) {
+ return tokenBase(baseUri).path(TokenService.class, "processLogin").build(realmId);
+ }
+
+ public static URI realmLoginPage(URI baseUri, String realmId) {
+ return tokenBase(baseUri).path(TokenService.class, "loginPage").build(realmId);
+ }
+
+ public static URI realmRegisterAction(URI baseUri, String realmId) {
+ return tokenBase(baseUri).path(TokenService.class, "processRegister").build(realmId);
+ }
+
+ public static URI realmRegisterPage(URI baseUri, String realmId) {
+ return tokenBase(baseUri).path(TokenService.class, "registerPage").build(realmId);
+ }
+
+ private static UriBuilder saasBase(URI baseUri) {
+ return UriBuilder.fromUri(baseUri).path(SaasService.class);
+ }
+
+ public static URI saasLoginAction(URI baseUri) {
+ return saasBase(baseUri).path(SaasService.class, "processLogin").build();
+ }
+
+ public static URI saasLoginPage(URI baseUri) {
+ return saasBase(baseUri).path(SaasService.class, "loginPage").build();
+ }
+
+ public static URI saasRegisterAction(URI baseUri) {
+ return saasBase(baseUri).path(SaasService.class, "processRegister").build();
+ }
+
+ public static URI saasRegisterPage(URI baseUri) {
+ return saasBase(baseUri).path(SaasService.class, "registerPage").build();
+ }
+
+ private static UriBuilder socialBase(URI baseUri) {
+ return UriBuilder.fromUri(baseUri).path(SocialResource.class);
+ }
+
+ public static URI socialCallback(URI baseUri) {
+ return socialBase(baseUri).path(SocialResource.class, "callback").build();
+ }
+
+ public static URI socialRedirectToProviderAuth(URI baseUri, String realmId) {
+ return socialBase(baseUri).path(SocialResource.class, "redirectToProviderAuth")
+ .build(realmId);
+ }
+
+}
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 af8b1aa..0a54871 100755
--- a/services/src/main/java/org/keycloak/services/resources/SaasService.java
+++ b/services/src/main/java/org/keycloak/services/resources/SaasService.java
@@ -14,9 +14,11 @@ import org.keycloak.services.models.RoleModel;
import org.keycloak.services.models.UserCredentialModel;
import org.keycloak.services.models.UserModel;
import org.keycloak.services.resources.admin.RealmsAdminResource;
+import org.keycloak.services.resources.flows.Flows;
import javax.ws.rs.*;
import javax.ws.rs.core.*;
+
import java.net.URI;
import java.util.StringTokenizer;
@@ -81,7 +83,8 @@ public class SaasService {
public Response callImpl() {
RealmManager realmManager = new RealmManager(session);
RealmModel realm = realmManager.defaultRealm();
- if (realm == null) throw new NotFoundException();
+ if (realm == null)
+ throw new NotFoundException();
UserModel user = authManager.authenticateSaasIdentityCookie(realm, uriInfo, headers);
if (user == null) {
return Response.status(401).build();
@@ -102,7 +105,8 @@ public class SaasService {
public Response callImpl() {
RealmManager realmManager = new RealmManager(session);
RealmModel realm = realmManager.defaultRealm();
- if (realm == null) throw new NotFoundException();
+ if (realm == null)
+ throw new NotFoundException();
UserModel user = authManager.authenticateSaasIdentityCookie(realm, uriInfo, headers);
if (user == null) {
return Response.status(401).build();
@@ -137,7 +141,6 @@ public class SaasService {
}.call();
}
-
public static UriBuilder contextRoot(UriInfo uriInfo) {
return UriBuilder.fromUri(uriInfo.getBaseUri()).replacePath("/auth-server");
}
@@ -153,7 +156,8 @@ public class SaasService {
protected RealmsAdminResource callImpl() {
RealmManager realmManager = new RealmManager(session);
RealmModel saasRealm = realmManager.defaultRealm();
- if (saasRealm == null) throw new NotFoundException();
+ if (saasRealm == null)
+ throw new NotFoundException();
UserModel admin = authManager.authenticateSaasIdentity(saasRealm, uriInfo, headers);
if (admin == null) {
throw new NotAuthorizedException("Bearer");
@@ -178,7 +182,8 @@ public class SaasService {
RealmManager realmManager = new RealmManager(session);
RealmModel realm = realmManager.defaultRealm();
authManager.expireSaasIdentityCookie(uriInfo);
- forwardToLoginForm(realm, null, null);
+
+ Flows.forms(realm, request).forwardToLogin();
}
}.run();
}
@@ -193,7 +198,8 @@ public class SaasService {
RealmManager realmManager = new RealmManager(session);
RealmModel realm = realmManager.defaultRealm();
authManager.expireSaasIdentityCookie(uriInfo);
- forwardToRegisterForm(realm, null, null);
+
+ Flows.forms(realm, request).forwardToRegistration();
}
}.run();
}
@@ -208,12 +214,12 @@ public class SaasService {
RealmManager realmManager = new RealmManager(session);
RealmModel realm = realmManager.defaultRealm();
authManager.expireSaasIdentityCookie(uriInfo);
- forwardToLoginForm(realm, null, null);
+
+ Flows.forms(realm, request).forwardToLogin();
}
}.run();
}
-
@Path("logout-cookie")
@GET
@NoCache
@@ -227,45 +233,6 @@ public class SaasService {
}.run();
}
- protected void forwardToLoginForm(RealmModel realm, String error, MultivaluedMap<String, String> formData) {
- if (error != null) {
- request.setAttribute("KEYCLOAK_LOGIN_ERROR_MESSAGE", error);
- }
-
- if (formData != null) {
- request.setAttribute("KEYCLOAK_FORM_DATA", formData);
- }
-
- forwardToForm(realm, Pages.loginForm);
- }
-
- protected void forwardToRegisterForm(RealmModel realm, String error, MultivaluedMap<String, String> formData) {
- if (error != null) {
- request.setAttribute("KEYCLOAK_LOGIN_ERROR_MESSAGE", error);
- }
-
- if (formData != null) {
- request.setAttribute("KEYCLOAK_FORM_DATA", formData);
- }
-
- forwardToForm(realm, Pages.registerForm);
- }
-
- protected void forwardToForm(RealmModel realm, String form) {
- request.setAttribute(RealmModel.class.getName(), realm);
-
- request.setAttribute("KEYCLOAK_LOGIN_PAGE", Urls.saasLoginPage(uriInfo));
- request.setAttribute("KEYCLOAK_LOGIN_ACTION", Urls.saasLoginAction(uriInfo));
-
- request.setAttribute("KEYCLOAK_REGISTRATION_PAGE", Urls.saasRegisterPage(uriInfo));
- request.setAttribute("KEYCLOAK_REGISTRATION_ACTION", Urls.saasRegisterAction(uriInfo));
-
- request.setAttribute("KEYCLOAK_SOCIAL_LOGIN", Urls.socialRedirectToProviderAuth(uriInfo, realm.getId()));
-
- request.forward(form);
- }
-
-
@Path("login")
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@@ -276,7 +243,8 @@ public class SaasService {
protected Response callImpl() {
RealmManager realmManager = new RealmManager(session);
RealmModel realm = realmManager.defaultRealm();
- if (realm == null) throw new NotFoundException();
+ if (realm == null)
+ throw new NotFoundException();
if (!realm.isEnabled()) {
throw new NotImplementedYetException();
@@ -285,26 +253,27 @@ public class SaasService {
UserModel user = realm.getUser(username);
if (user == null) {
logger.info("Not Authenticated! Incorrect user name");
- forwardToLoginForm(realm, "Invalid username or password", formData);
- return null;
+
+ return Flows.forms(realm, request).setError("Invalid username or password").setFormData(formData)
+ .forwardToLogin();
}
if (!user.isEnabled()) {
logger.info("NAccount is disabled, contact admin.");
- forwardToLoginForm(realm, "Account is disabled, contact admin.", formData);
- return null;
+
+ return Flows.forms(realm, request).setError("Invalid username or password")
+ .setFormData(formData).forwardToLogin();
}
boolean authenticated = authManager.authenticateForm(realm, user, formData);
if (!authenticated) {
logger.info("Not Authenticated! Invalid credentials");
- forwardToLoginForm(realm, "Invalid username or password", formData);
- return null;
+
+ return Flows.forms(realm, request).setError("Invalid username or password").setFormData(formData)
+ .forwardToLogin();
}
NewCookie cookie = authManager.createSaasIdentityCookie(realm, user, uriInfo);
- return Response.status(302)
- .cookie(cookie)
- .location(contextRoot(uriInfo).path(adminPath).build()).build();
+ return Response.status(302).cookie(cookie).location(contextRoot(uriInfo).path(adminPath).build()).build();
}
}.call();
}
@@ -340,8 +309,8 @@ public class SaasService {
String error = validateRegistrationForm(formData);
if (error != null) {
- forwardToRegisterForm(defaultRealm, error, formData);
- return null;
+ return Flows.forms(defaultRealm, request).setError(error).setFormData(formData)
+ .forwardToRegistration();
}
UserRepresentation newUser = new UserRepresentation();
@@ -366,16 +335,16 @@ public class SaasService {
last = token;
}
}
- if (first == null) first = new StringBuffer();
+ if (first == null)
+ first = new StringBuffer();
newUser.setFirstName(first.toString());
newUser.setLastName(last);
}
newUser.credential(CredentialRepresentation.PASSWORD, formData.getFirst("password"));
UserModel user = registerMe(defaultRealm, newUser);
if (user == null) {
- request.setAttribute("KEYCLOAK_LOGIN_ERROR_MESSAGE", "Username already exists.");
- forwardToRegisterForm(defaultRealm, "Username already exists.", formData);
- return null;
+ return Flows.forms(defaultRealm, request).setError("Username already exists.")
+ .setFormData(formData).forwardToRegistration();
}
NewCookie cookie = authManager.createSaasIdentityCookie(defaultRealm, user, uriInfo);
@@ -384,7 +353,6 @@ public class SaasService {
}.call();
}
-
protected UserModel registerMe(RealmModel defaultRealm, UserRepresentation newUser) {
if (!defaultRealm.isEnabled()) {
throw new ForbiddenException();
diff --git a/services/src/main/java/org/keycloak/services/resources/SocialResource.java b/services/src/main/java/org/keycloak/services/resources/SocialResource.java
index 35ed9ab..f29f908 100644
--- a/services/src/main/java/org/keycloak/services/resources/SocialResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/SocialResource.java
@@ -16,7 +16,6 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
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;
@@ -25,7 +24,11 @@ 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.RoleModel;
import org.keycloak.services.models.UserModel;
+import org.keycloak.services.resources.flows.Flows;
+import org.keycloak.services.resources.flows.OAuthFlows;
+import org.keycloak.services.resources.flows.Urls;
import org.keycloak.social.AuthCallback;
import org.keycloak.social.AuthRequest;
import org.keycloak.social.RequestDetails;
@@ -61,19 +64,6 @@ public class SocialResource {
this.socialRequestManager = socialRequestManager;
}
- public static UriBuilder socialServiceBaseUrl(UriInfo uriInfo) {
- UriBuilder base = uriInfo.getBaseUriBuilder().path(SocialResource.class);
- return base;
- }
-
- public static UriBuilder redirectToProviderAuthUrl(UriInfo uriInfo) {
- return socialServiceBaseUrl(uriInfo).path(SocialResource.class, "redirectToProviderAuth");
- }
-
- public static UriBuilder callbackUrl(UriInfo uriInfo) {
- return socialServiceBaseUrl(uriInfo).path(SocialResource.class, "callback");
- }
-
@GET
@Path("callback")
public Response callback() throws URISyntaxException {
@@ -86,41 +76,42 @@ public class SocialResource {
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().toString();
-
- SocialProviderConfig config = new SocialProviderConfig(key, secret, callbackUri);
+ RealmManager realmManager = new RealmManager(session);
+ RealmModel realm = realmManager.getRealm(realmId);
- AuthCallback callback = new AuthCallback(requestData.getSocialAttributes(), queryParams);
+ OAuthFlows oauth = Flows.oauth(realm, request, uriInfo, authManager, tokenManager);
- SocialUser socialUser = null;
- try {
- socialUser = provider.processCallback(config, callback);
- } catch (SocialProviderException e) {
- logger.warn("Failed to process social callback", e);
- OAuthUtil.securityFailureForward(request, "Failed to process social callback");
- return null;
+ if (!realm.isEnabled()) {
+ return oauth.forwardToSecurityFailure("Realm not enabled.");
}
- RealmManager realmManager = new RealmManager(session);
- RealmModel realm = realmManager.getRealm(realmId);
-
if (!realm.isEnabled()) {
- OAuthUtil.securityFailureForward(request, "Realm not enabled.");
- return null;
+ return oauth.forwardToSecurityFailure("Realm not enabled.");
}
String clientId = requestData.getClientAttributes().get("clientId");
UserModel client = realm.getUser(clientId);
if (client == null) {
- OAuthUtil.securityFailureForward(request, "Unknown login requester.");
- return null;
+ return oauth.forwardToSecurityFailure("Unknown login requester.");
}
if (!client.isEnabled()) {
- OAuthUtil.securityFailureForward(request, "Login requester not enabled.");
- return null;
+ return oauth.forwardToSecurityFailure("Login requester not enabled.");
+ }
+
+ String key = System.getProperty("keycloak.social." + requestData.getProviderId() + ".key");
+ String secret = System.getProperty("keycloak.social." + requestData.getProviderId() + ".secret");
+ String callbackUri = Urls.socialCallback(uriInfo.getBaseUri()).toString();
+ SocialProviderConfig config = new SocialProviderConfig(key, secret, callbackUri);
+
+ AuthCallback callback = new AuthCallback(requestData.getSocialAttributes(), queryParams);
+
+ SocialUser socialUser = null;
+ try {
+ socialUser = provider.processCallback(config, callback);
+ } catch (SocialProviderException e) {
+ logger.warn("Failed to process social callback", e);
+ return oauth.forwardToSecurityFailure("Failed to process social callback");
}
// TODO Lookup user based on attribute for provider id - this is so a user can have a friendly username + link a
@@ -133,20 +124,20 @@ public class SocialResource {
user.setAttribute(provider.getId() + ".id", socialUser.getId());
// TODO Grant default roles for realm when available
- realm.grantRole(user, realm.getRole("user"));
+ RoleModel defaultRole = realm.getRole("user");
+
+ realm.grantRole(user, defaultRole);
}
if (!user.isEnabled()) {
- OAuthUtil.securityFailureForward(request, "Your account is not enabled.");
- return null;
+ return oauth.forwardToSecurityFailure("Your account is not enabled.");
}
String scope = requestData.getClientAttributes().get("scope");
String state = requestData.getClientAttributes().get("state");
String redirectUri = requestData.getClientAttributes().get("redirectUri");
- return OAuthUtil.processAccessCode(realm, tokenManager, authManager, request, uriInfo, scope, state,
- redirectUri, client, user);
+ return oauth.processAccessCode(scope, state, redirectUri, client, user);
}
}.call();
}
@@ -159,13 +150,12 @@ public class SocialResource {
@QueryParam("redirect_uri") final String redirectUri) {
SocialProvider provider = getProvider(providerId);
if (provider == null) {
- OAuthUtil.securityFailureForward(request, "Social provider not found");
- return null;
+ return Flows.pages(request).forwardToSecurityFailure("Social provider not found");
}
String key = System.getProperty("keycloak.social." + providerId + ".key");
String secret = System.getProperty("keycloak.social." + providerId + ".secret");
- String callbackUri = callbackUrl(uriInfo).build().toString();
+ String callbackUri = Urls.socialCallback(uriInfo.getBaseUri()).toString();
SocialProviderConfig config = new SocialProviderConfig(key, secret, callbackUri);
@@ -181,9 +171,7 @@ public class SocialResource {
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;
+ return Flows.pages(request).forwardToSecurityFailure("Failed to redirect to social auth");
}
}
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 a9b1e8f..65b30ef 100755
--- a/services/src/main/java/org/keycloak/services/resources/TokenService.java
+++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java
@@ -10,6 +10,8 @@ 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.representations.idm.CredentialRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.services.managers.AccessCodeEntry;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.RealmManager;
@@ -17,9 +19,13 @@ import org.keycloak.services.managers.ResourceAdminManager;
import org.keycloak.services.managers.TokenManager;
import org.keycloak.services.models.RealmModel;
import org.keycloak.services.models.RoleModel;
+import org.keycloak.services.models.UserCredentialModel;
import org.keycloak.services.models.UserModel;
+import org.keycloak.services.resources.flows.Flows;
+import org.keycloak.services.resources.flows.OAuthFlows;
import javax.ws.rs.Consumes;
+import javax.ws.rs.ForbiddenException;
import javax.ws.rs.GET;
import javax.ws.rs.NotAuthorizedException;
import javax.ws.rs.POST;
@@ -30,14 +36,17 @@ 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.NewCookie;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.Providers;
+
import java.security.PrivateKey;
import java.util.HashMap;
import java.util.Map;
+import java.util.StringTokenizer;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -72,8 +81,7 @@ public class TokenService {
}
public static UriBuilder tokenServiceBaseUrl(UriInfo uriInfo) {
- UriBuilder base = uriInfo.getBaseUriBuilder()
- .path(RealmsResource.class).path(RealmsResource.class, "getTokenService");
+ UriBuilder base = uriInfo.getBaseUriBuilder().path(RealmsResource.class).path(RealmsResource.class, "getTokenService");
return base;
}
@@ -104,7 +112,6 @@ public class TokenService {
return tokenServiceBaseUrl(uriInfo).path(TokenService.class, "processOAuth");
}
-
@Path("grants/identity-token")
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@@ -174,52 +181,126 @@ public class TokenService {
@Path("auth/request/login")
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
- public Response processLogin(final MultivaluedMap<String, String> formData) {
+ public Response processLogin(@QueryParam("client_id") final String clientId, @QueryParam("scope") final String scopeParam,
+ @QueryParam("state") final String state, @QueryParam("redirect_uri") final String redirect,
+ final MultivaluedMap<String, String> formData) {
return new Transaction() {
protected Response callImpl() {
- String clientId = formData.getFirst("client_id");
- String scopeParam = formData.getFirst("scope");
- String state = formData.getFirst("state");
- String redirect = formData.getFirst("redirect_uri");
+ OAuthFlows oauth = Flows.oauth(realm, request, uriInfo, authManager, tokenManager);
if (!realm.isEnabled()) {
- OAuthUtil.securityFailureForward(request, "Realm not enabled.");
- return null;
+ return oauth.forwardToSecurityFailure("Realm not enabled.");
}
UserModel client = realm.getUser(clientId);
if (client == null) {
- OAuthUtil.securityFailureForward(request, "Unknown login requester.");
- return null;
+ return oauth.forwardToSecurityFailure("Unknown login requester.");
}
if (!client.isEnabled()) {
- OAuthUtil.securityFailureForward(request, "Login requester not enabled.");
- return null;
+ return oauth.forwardToSecurityFailure("Login requester not enabled.");
}
String username = formData.getFirst("username");
UserModel user = realm.getUser(username);
if (user == null) {
logger.error("Incorrect user name.");
- request.setAttribute("KEYCLOAK_LOGIN_ERROR_MESSAGE", "Invalid username or password");
- request.setAttribute("KEYCLOAK_FORM_DATA", formData);
- OAuthUtil.forwardToLoginForm(realm, request, uriInfo, redirect, clientId, scopeParam, state);
- return null;
+
+ return Flows.forms(realm, request).setError("Invalid username or password").setFormData(formData)
+ .forwardToLogin();
}
if (!user.isEnabled()) {
- OAuthUtil.securityFailureForward(request, "Your account is not enabled.");
- return null;
+ return oauth.forwardToSecurityFailure("Your account is not enabled.");
}
boolean authenticated = authManager.authenticateForm(realm, user, formData);
if (!authenticated) {
logger.error("Authentication failed");
- request.setAttribute("username", username);
- request.setAttribute("KEYCLOAK_LOGIN_ERROR_MESSAGE", "Invalid username or password");
- request.setAttribute("KEYCLOAK_FORM_DATA", formData);
- OAuthUtil.forwardToLoginForm(realm, request, uriInfo, redirect, clientId, scopeParam, state);
- return null;
+
+ return Flows.forms(realm, request).setError("Invalid username or password").setFormData(formData)
+ .forwardToLogin();
}
- return OAuthUtil.processAccessCode(realm, tokenManager, authManager, request, uriInfo, scopeParam, state,
- redirect, client, user);
+ return oauth.processAccessCode(scopeParam, state, redirect, client, user);
+ }
+ }.call();
+ }
+
+ @Path("registrations")
+ @POST
+ @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+ public Response processRegister(@QueryParam("client_id") final String clientId,
+ @QueryParam("scope") final String scopeParam, @QueryParam("state") final String state,
+ @QueryParam("redirect_uri") final String redirect, final MultivaluedMap<String, String> formData) {
+ return new Transaction() {
+ @Override
+ protected Response callImpl() {
+ OAuthFlows oauth = Flows.oauth(realm, request, uriInfo, authManager, tokenManager);
+
+ if (!realm.isEnabled()) {
+ return oauth.forwardToSecurityFailure("Realm not enabled");
+ }
+ UserModel client = realm.getUser(clientId);
+ if (client == null) {
+ return oauth.forwardToSecurityFailure("Unknown login requester.");
+ }
+
+ if (!client.isEnabled()) {
+ return oauth.forwardToSecurityFailure("Login requester not enabled.");
+ }
+
+ if (!realm.isRegistrationAllowed()) {
+ return oauth.forwardToSecurityFailure("Registration not allowed");
+ }
+
+ String error = validateRegistrationForm(formData);
+ if (error != null) {
+ return Flows.forms(realm, request).setError(error).setFormData(formData).forwardToRegistration();
+ }
+
+ String username = formData.getFirst("username");
+
+ UserModel user = realm.getUser(username);
+ if (user != null) {
+ return Flows.forms(realm, request).setError("Username already exists.").setFormData(formData)
+ .forwardToRegistration();
+ }
+
+ user = realm.addUser(username);
+
+ String fullname = formData.getFirst("name");
+ if (fullname != null) {
+ StringTokenizer tokenizer = new StringTokenizer(fullname, " ");
+ StringBuffer first = null;
+ String last = "";
+ while (tokenizer.hasMoreTokens()) {
+ String token = tokenizer.nextToken();
+ if (tokenizer.hasMoreTokens()) {
+ if (first == null) {
+ first = new StringBuffer();
+ } else {
+ first.append(" ");
+ }
+ first.append(token);
+ } else {
+ last = token;
+ }
+ }
+ if (first == null)
+ first = new StringBuffer();
+ user.setFirstName(first.toString());
+ user.setLastName(last);
+ }
+
+ user.setEmail(formData.getFirst("email"));
+
+ UserCredentialModel credentials = new UserCredentialModel();
+ credentials.setType(CredentialRepresentation.PASSWORD);
+ credentials.setValue(formData.getFirst("password"));
+ realm.updateCredential(user, credentials);
+
+ // TODO Grant default roles for realm when available
+ RoleModel defaultRole = realm.getRole("user");
+
+ realm.grantRole(user, defaultRole);
+
+ return processLogin(clientId, scopeParam, state, redirect, formData);
}
}.call();
}
@@ -276,7 +357,6 @@ public class TokenService {
return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build();
}
-
JWSInput input = new JWSInput(code, providers);
boolean verifiedCode = false;
try {
@@ -288,7 +368,8 @@ public class TokenService {
Map<String, String> res = new HashMap<String, String>();
res.put("error", "invalid_grant");
res.put("error_description", "Unable to verify code signature");
- return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build();
+ return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res)
+ .build();
}
String key = input.readContent(String.class);
AccessCodeEntry accessCode = tokenManager.pullAccessCode(key);
@@ -296,25 +377,29 @@ public class TokenService {
Map<String, String> res = new HashMap<String, String>();
res.put("error", "invalid_grant");
res.put("error_description", "Code not found");
- return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build();
+ return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res)
+ .build();
}
if (accessCode.isExpired()) {
Map<String, String> res = new HashMap<String, String>();
res.put("error", "invalid_grant");
res.put("error_description", "Code is expired");
- return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build();
+ return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res)
+ .build();
}
if (!accessCode.getToken().isActive()) {
Map<String, String> res = new HashMap<String, String>();
res.put("error", "invalid_grant");
res.put("error_description", "Token expired");
- return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build();
+ return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res)
+ .build();
}
if (!client.getLoginName().equals(accessCode.getClient().getLoginName())) {
Map<String, String> res = new HashMap<String, String>();
res.put("error", "invalid_grant");
res.put("error_description", "Auth error");
- return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build();
+ return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res)
+ .build();
}
logger.info("accessRequest SUCCESS");
AccessTokenResponse res = accessTokenResponse(realm.getPrivateKey(), accessCode.getToken());
@@ -331,9 +416,7 @@ public class TokenService {
} catch (Exception e) {
throw new RuntimeException(e);
}
- String encodedToken = new JWSBuilder()
- .content(tokenBytes)
- .rsa256(privateKey);
+ String encodedToken = new JWSBuilder().content(tokenBytes).rsa256(privateKey);
return accessTokenResponse(token, encodedToken);
}
@@ -352,25 +435,25 @@ public class TokenService {
@Path("login")
@GET
public Response loginPage(final @QueryParam("response_type") String responseType,
- final @QueryParam("redirect_uri") String redirect,
- final @QueryParam("client_id") String clientId,
- final @QueryParam("scope") String scopeParam,
- final @QueryParam("state") String state) {
+ final @QueryParam("redirect_uri") String redirect, final @QueryParam("client_id") String clientId,
+ final @QueryParam("scope") String scopeParam, final @QueryParam("state") String state) {
return new Transaction() {
protected Response callImpl() {
+ OAuthFlows oauth = Flows.oauth(realm, request, uriInfo, authManager, tokenManager);
+
if (!realm.isEnabled()) {
- OAuthUtil.securityFailureForward(request, "Realm not enabled");
+ oauth.forwardToSecurityFailure("Realm not enabled");
return null;
}
UserModel client = realm.getUser(clientId);
if (client == null) {
- OAuthUtil.securityFailureForward(request, "Unknown login requester.");
+ oauth.forwardToSecurityFailure("Unknown login requester.");
transaction.rollback();
return null;
}
if (!client.isEnabled()) {
- OAuthUtil.securityFailureForward(request, "Login requester not enabled.");
+ oauth.forwardToSecurityFailure("Login requester not enabled.");
transaction.rollback();
session.close();
return null;
@@ -380,7 +463,7 @@ public class TokenService {
RoleModel identityRequestRole = realm.getRole(RealmManager.IDENTITY_REQUESTER_ROLE);
boolean isResource = realm.hasRole(client, resourceRole);
if (!isResource && !realm.hasRole(client, identityRequestRole)) {
- OAuthUtil.securityFailureForward(request, "Login requester not allowed to request login.");
+ oauth.forwardToSecurityFailure("Login requester not allowed to request login.");
transaction.rollback();
session.close();
return null;
@@ -389,12 +472,42 @@ public class TokenService {
UserModel user = authManager.authenticateIdentityCookie(realm, uriInfo, headers);
if (user != null) {
logger.info(user.getLoginName() + " already logged in.");
- return OAuthUtil.processAccessCode(realm, tokenManager, authManager, request, uriInfo, scopeParam, state,
- redirect, client, user);
+ return oauth.processAccessCode(scopeParam, state, redirect, client, user);
}
- OAuthUtil.forwardToLoginForm(realm, request, uriInfo, redirect, clientId, scopeParam, state);
- return null;
+ return Flows.forms(realm, request).forwardToLogin();
+ }
+ }.call();
+ }
+
+ @Path("registrations")
+ @GET
+ public Response registerPage(final @QueryParam("response_type") String responseType,
+ final @QueryParam("redirect_uri") String redirect, final @QueryParam("client_id") String clientId,
+ final @QueryParam("scope") String scopeParam, final @QueryParam("state") String state) {
+ return new Transaction() {
+ protected Response callImpl() {
+ OAuthFlows oauth = Flows.oauth(realm, request, uriInfo, authManager, tokenManager);
+
+ if (!realm.isEnabled()) {
+ return oauth.forwardToSecurityFailure("Realm not enabled");
+ }
+ UserModel client = realm.getUser(clientId);
+ if (client == null) {
+ return oauth.forwardToSecurityFailure("Unknown login requester.");
+ }
+
+ if (!client.isEnabled()) {
+ return oauth.forwardToSecurityFailure("Login requester not enabled.");
+ }
+
+ if (!realm.isRegistrationAllowed()) {
+ return oauth.forwardToSecurityFailure("Registration not allowed");
+ }
+
+ authManager.expireIdentityCookie(realm, uriInfo);
+
+ return Flows.forms(realm, request).forwardToRegistration();
}
}.call();
}
@@ -425,6 +538,8 @@ public class TokenService {
public Response processOAuth(final MultivaluedMap<String, String> formData) {
return new Transaction() {
protected Response callImpl() {
+ OAuthFlows oauth = Flows.oauth(realm, request, uriInfo, authManager, tokenManager);
+
String code = formData.getFirst("code");
JWSInput input = new JWSInput(code, providers);
boolean verifiedCode = false;
@@ -434,16 +549,12 @@ public class TokenService {
logger.debug("Failed to verify signature", ignored);
}
if (!verifiedCode) {
- OAuthUtil.securityFailureForward(request, "Illegal access code.");
- session.close();
- return null;
+ return oauth.forwardToSecurityFailure("Illegal access code.");
}
String key = input.readContent(String.class);
AccessCodeEntry accessCodeEntry = tokenManager.getAccessCode(key);
if (accessCodeEntry == null) {
- OAuthUtil.securityFailureForward(request, "Unknown access code.");
- session.close();
- return null;
+ return oauth.forwardToSecurityFailure("Unknown access code.");
}
String redirect = accessCodeEntry.getRedirectUri();
@@ -453,16 +564,45 @@ public class TokenService {
return redirectAccessDenied(redirect, state);
}
- return OAuthUtil.redirectAccessCode(realm, authManager, uriInfo, accessCodeEntry, state, redirect);
+ return oauth.redirectAccessCode(accessCodeEntry, state, redirect);
}
}.call();
}
protected Response redirectAccessDenied(String redirect, String state) {
UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam("error", "access_denied");
- if (state != null) redirectUri.queryParam("state", state);
+ if (state != null)
+ redirectUri.queryParam("state", state);
Response.ResponseBuilder location = Response.status(302).location(redirectUri.build());
return location.build();
}
+ private String validateRegistrationForm(MultivaluedMap<String, String> formData) {
+ if (isEmpty(formData.getFirst("name"))) {
+ return "Please specify full name";
+ }
+
+ if (isEmpty(formData.getFirst("email"))) {
+ return "Please specify email";
+ }
+
+ if (isEmpty(formData.getFirst("username"))) {
+ return "Please specify username";
+ }
+
+ if (isEmpty(formData.getFirst("password"))) {
+ return "Please specify password";
+ }
+
+ if (!formData.getFirst("password").equals(formData.getFirst("password-confirm"))) {
+ return "Password confirmation doesn't match.";
+ }
+
+ return null;
+ }
+
+ private boolean isEmpty(String s) {
+ return s == null || s.length() == 0;
+ }
+
}