keycloak-aplcache
Changes
forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java 2(+1 -1)
services/pom.xml 5(+0 -5)
Details
diff --git a/forms/login-api/src/main/java/org/keycloak/login/LoginFormsProvider.java b/forms/login-api/src/main/java/org/keycloak/login/LoginFormsProvider.java
index 1aede52..3e1eb51 100755
--- a/forms/login-api/src/main/java/org/keycloak/login/LoginFormsProvider.java
+++ b/forms/login-api/src/main/java/org/keycloak/login/LoginFormsProvider.java
@@ -36,7 +36,7 @@ public interface LoginFormsProvider extends Provider {
public Response createCode();
- public LoginFormsProvider setAccessCode(String accessCode);
+ public LoginFormsProvider setClientSessionCode(String accessCode);
public LoginFormsProvider setAccessRequest(List<RoleModel> realmRolesRequested, MultivaluedMap<String,RoleModel> resourceRolesRequested);
diff --git a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java
index 343c3c1..9a76e78 100755
--- a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java
+++ b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java
@@ -293,7 +293,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
}
@Override
- public LoginFormsProvider setAccessCode(String accessCode) {
+ public LoginFormsProvider setClientSessionCode(String accessCode) {
this.accessCode = accessCode;
return this;
}
services/pom.xml 5(+0 -5)
diff --git a/services/pom.xml b/services/pom.xml
index 0e5e597..e2390b7 100755
--- a/services/pom.xml
+++ b/services/pom.xml
@@ -15,11 +15,6 @@
<dependencies>
<dependency>
- <groupId>org.picketlink</groupId>
- <artifactId>picketlink-federation</artifactId>
- <version>2.7.0-SNAPSHOT</version>
- </dependency>
- <dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk16</artifactId>
<scope>provided</scope>
diff --git a/services/src/main/java/org/keycloak/protocol/LoginProtocol.java b/services/src/main/java/org/keycloak/protocol/LoginProtocol.java
new file mode 100755
index 0000000..6790adc
--- /dev/null
+++ b/services/src/main/java/org/keycloak/protocol/LoginProtocol.java
@@ -0,0 +1,37 @@
+package org.keycloak.protocol;
+
+import org.jboss.resteasy.spi.HttpRequest;
+import org.keycloak.ClientConnection;
+import org.keycloak.events.EventBuilder;
+import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.provider.Provider;
+import org.keycloak.services.managers.AuthenticationManager;
+import org.keycloak.protocol.oidc.OAuthFlows;
+import org.keycloak.services.managers.ClientSessionCode;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface LoginProtocol extends Provider {
+ OAuthFlows setSession(KeycloakSession session);
+
+ OAuthFlows setRealm(RealmModel realm);
+
+ OAuthFlows setRequest(HttpRequest request);
+
+ OAuthFlows setUriInfo(UriInfo uriInfo);
+
+ OAuthFlows setClientConnection(ClientConnection clientConnection);
+
+ Response cancelLogin(ClientSessionModel clientSession);
+ Response invalidSessionError(ClientSessionModel clientSession);
+ Response authenticated(UserSessionModel userSession, ClientSessionCode accessCode);
+ Response consentDenied(ClientSessionModel clientSession);
+}
diff --git a/services/src/main/java/org/keycloak/protocol/LoginProtocolFactory.java b/services/src/main/java/org/keycloak/protocol/LoginProtocolFactory.java
new file mode 100755
index 0000000..817b224
--- /dev/null
+++ b/services/src/main/java/org/keycloak/protocol/LoginProtocolFactory.java
@@ -0,0 +1,10 @@
+package org.keycloak.protocol;
+
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface LoginProtocolFactory extends ProviderFactory<LoginProtocol> {
+}
diff --git a/services/src/main/java/org/keycloak/protocol/LoginProtocolSpi.java b/services/src/main/java/org/keycloak/protocol/LoginProtocolSpi.java
new file mode 100755
index 0000000..ef73b34
--- /dev/null
+++ b/services/src/main/java/org/keycloak/protocol/LoginProtocolSpi.java
@@ -0,0 +1,27 @@
+package org.keycloak.protocol;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class LoginProtocolSpi implements Spi {
+
+ @Override
+ public String getName() {
+ return "login-protocol";
+ }
+
+ @Override
+ public Class<? extends Provider> getProviderClass() {
+ return LoginProtocol.class;
+ }
+
+ @Override
+ public Class<? extends ProviderFactory> getProviderFactoryClass() {
+ return LoginProtocolFactory.class;
+ }
+
+}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OAuthFlows.java b/services/src/main/java/org/keycloak/protocol/oidc/OAuthFlows.java
new file mode 100755
index 0000000..1496070
--- /dev/null
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OAuthFlows.java
@@ -0,0 +1,178 @@
+/*
+ * 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.protocol.oidc;
+
+import org.jboss.logging.Logger;
+import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
+import org.jboss.resteasy.spi.HttpRequest;
+import org.keycloak.ClientConnection;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.events.EventBuilder;
+import org.keycloak.events.Details;
+import org.keycloak.events.EventType;
+import org.keycloak.login.LoginFormsProvider;
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RequiredCredentialModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserModel.RequiredAction;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.services.managers.AuthenticationManager;
+import org.keycloak.services.managers.ClientSessionCode;
+import org.keycloak.protocol.LoginProtocol;
+import org.keycloak.services.resources.flows.Flows;
+
+import javax.ws.rs.core.Cookie;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class OAuthFlows implements LoginProtocol {
+
+ public static final String LOGIN_PAGE_PROTOCOL = "openid-connect";
+ public static final String STATE_PARAM = "state";
+ public static final String SCOPE_PARAM = "scope";
+ public static final String RESPONSE_TYPE_PARAM = "response_type";
+ public static final String REDIRECT_URI_PARAM = "redirect_uri";
+ public static final String CLIENT_ID_PARAM = "client_id";
+ public static final String PROMPT_PARAM = "prompt";
+ public static final String LOGIN_HINT_PARAM = "login_hint";
+ private static final Logger log = Logger.getLogger(OAuthFlows.class);
+
+ protected KeycloakSession session;
+
+ protected RealmModel realm;
+
+ protected HttpRequest request;
+
+ protected UriInfo uriInfo;
+
+ protected ClientConnection clientConnection;
+
+ public OAuthFlows(KeycloakSession session, RealmModel realm, HttpRequest request, UriInfo uriInfo,
+ ClientConnection clientConnection) {
+ this.session = session;
+ this.realm = realm;
+ this.request = request;
+ this.uriInfo = uriInfo;
+ this.clientConnection = clientConnection;
+ }
+
+ public OAuthFlows() {
+ }
+
+ @Override
+ public OAuthFlows setSession(KeycloakSession session) {
+ this.session = session;
+ return this;
+ }
+
+ @Override
+ public OAuthFlows setRealm(RealmModel realm) {
+ this.realm = realm;
+ return this;
+ }
+
+ @Override
+ public OAuthFlows setRequest(HttpRequest request) {
+ this.request = request;
+ return this;
+ }
+
+ @Override
+ public OAuthFlows setUriInfo(UriInfo uriInfo) {
+ this.uriInfo = uriInfo;
+ return this;
+ }
+
+ @Override
+ public OAuthFlows setClientConnection(ClientConnection clientConnection) {
+ this.clientConnection = clientConnection;
+ return this;
+ }
+
+ @Override
+ public Response cancelLogin(ClientSessionModel clientSession) {
+ String redirect = clientSession.getRedirectUri();
+ String state = clientSession.getNote(OAuthFlows.STATE_PARAM);
+ UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam(OAuth2Constants.ERROR, "access_denied");
+ if (state != null) {
+ redirectUri.queryParam(OAuth2Constants.STATE, state);
+ }
+ return Response.status(302).location(redirectUri.build()).build();
+ }
+
+ @Override
+ public Response authenticated(UserSessionModel userSession, ClientSessionCode accessCode) {
+ ClientSessionModel clientSession = accessCode.getClientSession();
+ String redirect = clientSession.getRedirectUri();
+ String state = clientSession.getNote(OAuthFlows.STATE_PARAM);
+ accessCode.setAction(ClientSessionModel.Action.CODE_TO_TOKEN);
+ UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam(OAuth2Constants.CODE, accessCode.getCode());
+ log.debugv("redirectAccessCode: state: {0}", state);
+ if (state != null)
+ redirectUri.queryParam(OAuth2Constants.STATE, state);
+ Response.ResponseBuilder location = Response.status(302).location(redirectUri.build());
+
+ return location.build();
+ }
+
+ public Response consentDenied(ClientSessionModel clientSession) {
+ String redirect = clientSession.getRedirectUri();
+ String state = clientSession.getNote(OAuthFlows.STATE_PARAM);
+ UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam(OAuth2Constants.ERROR, "access_denied");
+ if (state != null)
+ redirectUri.queryParam(OAuth2Constants.STATE, state);
+ Response.ResponseBuilder location = Response.status(302).location(redirectUri.build());
+ return location.build();
+ }
+
+
+ public Response invalidSessionError(ClientSessionModel clientSession) {
+ String redirect = clientSession.getRedirectUri();
+ String state = clientSession.getNote(OAuthFlows.STATE_PARAM);
+ UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam(OAuth2Constants.ERROR, "access_denied");
+ if (state != null) {
+ redirectUri.queryParam(OAuth2Constants.STATE, state);
+ }
+ return Response.status(302).location(redirectUri.build()).build();
+ }
+
+ @Override
+ public void close() {
+
+ }
+}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnectFactory.java b/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnectFactory.java
new file mode 100755
index 0000000..5c35daf
--- /dev/null
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnectFactory.java
@@ -0,0 +1,32 @@
+package org.keycloak.protocol.oidc;
+
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.protocol.LoginProtocol;
+import org.keycloak.protocol.LoginProtocolFactory;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class OpenIDConnectFactory implements LoginProtocolFactory {
+ @Override
+ public LoginProtocol create(KeycloakSession session) {
+ return new OAuthFlows().setSession(session);
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+
+ }
+
+ @Override
+ public void close() {
+
+ }
+
+ @Override
+ public String getId() {
+ return "openid-connect";
+ }
+}
diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
index ae0b20c..f64fc0a 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -1,20 +1,32 @@
package org.keycloak.services.managers;
import org.jboss.logging.Logger;
+import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
+import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.ClientConnection;
import org.keycloak.RSATokenVerifier;
import org.keycloak.VerificationException;
+import org.keycloak.events.Details;
+import org.keycloak.events.EventBuilder;
+import org.keycloak.events.EventType;
import org.keycloak.jose.jws.JWSBuilder;
+import org.keycloak.login.LoginFormsProvider;
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredCredentialModel;
+import org.keycloak.models.RoleModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.protocol.LoginProtocol;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.services.resources.RealmsResource;
+import org.keycloak.services.resources.flows.Flows;
import org.keycloak.services.util.CookieHelper;
import org.keycloak.util.Time;
@@ -22,12 +34,14 @@ import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.NewCookie;
+import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import java.net.URI;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
+import java.util.UUID;
/**
* Stateless object that manages authentication
@@ -78,7 +92,7 @@ public class AuthenticationManager {
}
- public AccessToken createIdentityToken(RealmModel realm, UserModel user, UserSessionModel session) {
+ public static AccessToken createIdentityToken(RealmModel realm, UserModel user, UserSessionModel session) {
AccessToken token = new AccessToken();
token.id(KeycloakModelUtils.generateId());
token.issuedNow();
@@ -93,7 +107,7 @@ public class AuthenticationManager {
return token;
}
- public void createLoginCookie(RealmModel realm, UserModel user, UserSessionModel session, UriInfo uriInfo, ClientConnection connection) {
+ public static void createLoginCookie(RealmModel realm, UserModel user, UserSessionModel session, UriInfo uriInfo, ClientConnection connection) {
String cookiePath = getIdentityCookiePath(realm, uriInfo);
AccessToken identityToken = createIdentityToken(realm, user, session);
String encoded = encodeToken(realm, identityToken);
@@ -116,7 +130,7 @@ public class AuthenticationManager {
}
- public void createRememberMeCookie(RealmModel realm, String username, UriInfo uriInfo, ClientConnection connection) {
+ public static void createRememberMeCookie(RealmModel realm, String username, UriInfo uriInfo, ClientConnection connection) {
String path = getIdentityCookiePath(realm, uriInfo);
boolean secureOnly = realm.getSslRequired().isRequired(connection);
// remember me cookie should be persistent (hardcoded to 365 days for now)
@@ -124,7 +138,7 @@ public class AuthenticationManager {
CookieHelper.addCookie(KEYCLOAK_REMEMBER_ME, username, path, null, null, 31536000, secureOnly, true);
}
- protected String encodeToken(RealmModel realm, Object token) {
+ protected static String encodeToken(RealmModel realm, Object token) {
String encodedToken = new JWSBuilder()
.jsonContent(token)
.rsa256(realm.getPrivateKey());
@@ -180,6 +194,114 @@ public class AuthenticationManager {
return authResult;
}
+ public static Response redirectAfterSuccessfulFlow(KeycloakSession session, RealmModel realm, UserSessionModel userSession,
+ ClientSessionModel clientSession,
+ HttpRequest request, UriInfo uriInfo, ClientConnection clientConnection) {
+ Cookie sessionCookie = request.getHttpHeaders().getCookies().get(AuthenticationManager.KEYCLOAK_SESSION_COOKIE);
+ if (sessionCookie != null) {
+
+ String[] split = sessionCookie.getValue().split("/");
+ if (split.length >= 3) {
+ String oldSessionId = split[2];
+ if (!oldSessionId.equals(userSession.getId())) {
+ UserSessionModel oldSession = session.sessions().getUserSession(realm, oldSessionId);
+ if (oldSession != null) {
+ logger.debugv("Removing old user session: session: {0}", oldSessionId);
+ session.sessions().removeUserSession(realm, oldSession);
+ }
+ }
+ }
+ }
+
+ // refresh the cookies!
+ createLoginCookie(realm, userSession.getUser(), userSession, uriInfo, clientConnection);
+ if (userSession.isRememberMe()) createRememberMeCookie(realm, userSession.getUser().getUsername(), uriInfo, clientConnection);
+ LoginProtocol protocol = session.getProvider(LoginProtocol.class, clientSession.getAuthMethod());
+ protocol.setRealm(realm)
+ .setRequest(request)
+ .setUriInfo(uriInfo)
+ .setClientConnection(clientConnection);
+ return protocol.authenticated(userSession, new ClientSessionCode(realm, clientSession));
+
+ }
+
+ public static Response nextActionAfterAuthentication(KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession,
+ ClientConnection clientConnection,
+ HttpRequest request, UriInfo uriInfo, EventBuilder event) {
+ RealmModel realm = clientSession.getRealm();
+ UserModel user = userSession.getUser();
+ isTotpConfigurationRequired(realm, user);
+ isEmailVerificationRequired(realm, user);
+ ClientModel client = clientSession.getClient();
+
+ boolean isResource = client instanceof ApplicationModel;
+ ClientSessionCode accessCode = new ClientSessionCode(realm, clientSession);
+
+
+ logger.debugv("processAccessCode: isResource: {0}", isResource);
+ logger.debugv("processAccessCode: go to oauth page?: {0}",
+ !isResource);
+
+ event.detail(Details.CODE_ID, clientSession.getId());
+
+ Set<UserModel.RequiredAction> requiredActions = user.getRequiredActions();
+ if (!requiredActions.isEmpty()) {
+ UserModel.RequiredAction action = user.getRequiredActions().iterator().next();
+ accessCode.setRequiredAction(action);
+
+ LoginFormsProvider loginFormsProvider = Flows.forms(session, realm, client, uriInfo).setClientSessionCode(accessCode.getCode()).setUser(user);
+ if (action.equals(UserModel.RequiredAction.VERIFY_EMAIL)) {
+ String key = UUID.randomUUID().toString();
+ clientSession.setNote("key", key);
+ loginFormsProvider.setVerifyCode(key);
+ event.clone().event(EventType.SEND_VERIFY_EMAIL).detail(Details.EMAIL, user.getEmail()).success();
+ }
+
+ return loginFormsProvider
+ .createResponse(action);
+ }
+
+ if (!isResource) {
+ accessCode.setAction(ClientSessionModel.Action.OAUTH_GRANT);
+
+ List<RoleModel> realmRoles = new LinkedList<RoleModel>();
+ MultivaluedMap<String, RoleModel> resourceRoles = new MultivaluedMapImpl<String, RoleModel>();
+ for (RoleModel r : accessCode.getRequestedRoles()) {
+ if (r.getContainer() instanceof RealmModel) {
+ realmRoles.add(r);
+ } else {
+ resourceRoles.add(((ApplicationModel) r.getContainer()).getName(), r);
+ }
+ }
+
+ return Flows.forms(session, realm, client, uriInfo)
+ .setClientSessionCode(accessCode.getCode())
+ .setAccessRequest(realmRoles, resourceRoles)
+ .setClient(client)
+ .createOAuthGrant();
+ }
+
+ event.success();
+ return redirectAfterSuccessfulFlow(session, realm , userSession, clientSession, request, uriInfo, clientConnection);
+
+ }
+
+ protected static void isTotpConfigurationRequired(RealmModel realm, UserModel user) {
+ for (RequiredCredentialModel c : realm.getRequiredCredentials()) {
+ if (c.getType().equals(CredentialRepresentation.TOTP) && !user.isTotp()) {
+ user.addRequiredAction(UserModel.RequiredAction.CONFIGURE_TOTP);
+ logger.debug("User is required to configure totp");
+ }
+ }
+ }
+
+ protected static void isEmailVerificationRequired(RealmModel realm, UserModel user) {
+ if (realm.isVerifyEmail() && !user.isEmailVerified()) {
+ user.addRequiredAction(UserModel.RequiredAction.VERIFY_EMAIL);
+ logger.debug("User is required to verify email");
+ }
+ }
+
protected AuthResult verifyIdentityToken(KeycloakSession session, RealmModel realm, UriInfo uriInfo, ClientConnection connection, boolean checkActive, String tokenString) {
try {
AccessToken token = RSATokenVerifier.verifyToken(tokenString, realm.getPublicKey(), realm.getName(), checkActive);
diff --git a/services/src/main/java/org/keycloak/services/resources/AccountService.java b/services/src/main/java/org/keycloak/services/resources/AccountService.java
index d343af6..d6cc1b8 100755
--- a/services/src/main/java/org/keycloak/services/resources/AccountService.java
+++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java
@@ -25,7 +25,6 @@ import org.jboss.logging.Logger;
import org.jboss.resteasy.spi.BadRequestException;
import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.ClientConnection;
-import org.keycloak.OAuth2Constants;
import org.keycloak.account.AccountPages;
import org.keycloak.account.AccountProvider;
import org.keycloak.events.EventBuilder;
@@ -56,8 +55,8 @@ import org.keycloak.services.managers.Auth;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.services.messages.Messages;
-import org.keycloak.services.protocol.OpenIdConnectProtocol;
import org.keycloak.services.resources.flows.Flows;
+import org.keycloak.protocol.oidc.OAuthFlows;
import org.keycloak.services.resources.flows.OAuthRedirect;
import org.keycloak.services.resources.flows.Urls;
import org.keycloak.services.util.CookieHelper;
@@ -658,7 +657,7 @@ public class AccountService {
ClientSessionModel clientSession = auth.getClientSession();
clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE);
clientSession.setRedirectUri(redirectUri);
- clientSession.setNote(OpenIdConnectProtocol.STATE_PARAM, UUID.randomUUID().toString());
+ clientSession.setNote(OAuthFlows.STATE_PARAM, UUID.randomUUID().toString());
ClientSessionCode clientSessionCode = new ClientSessionCode(realm, clientSession);
return Flows.social(realm, uriInfo, clientConnection, provider)
.redirectToSocialProvider(clientSessionCode);
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
index 4c2c6e3..3fc6f68 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
@@ -20,7 +20,6 @@ import org.keycloak.models.SocialLinkModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
-import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.representations.adapters.action.UserStats;
@@ -31,14 +30,13 @@ import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.SocialLinkRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.representations.idm.UserSessionRepresentation;
-import org.keycloak.services.managers.AccessCode;
import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.managers.ResourceAdminManager;
import org.keycloak.services.managers.TokenManager;
import org.keycloak.services.managers.UserManager;
-import org.keycloak.services.protocol.OpenIdConnectProtocol;
import org.keycloak.services.resources.flows.Flows;
+import org.keycloak.protocol.oidc.OAuthFlows;
import org.keycloak.services.resources.flows.Urls;
import javax.ws.rs.Consumes;
@@ -897,7 +895,7 @@ public class UsersResource {
UserSessionModel userSession = session.sessions().createUserSession(realm, user, username, clientConnection.getRemoteAddr(), "form", false);
//audit.session(userSession);
ClientSessionModel clientSession = session.sessions().createClientSession(realm, client);
- clientSession.setAuthMethod(OpenIdConnectProtocol.LOGIN_PAGE_PROTOCOL);
+ clientSession.setAuthMethod(OAuthFlows.LOGIN_PAGE_PROTOCOL);
clientSession.setRedirectUri(redirect);
clientSession.setUserSession(userSession);
ClientSessionCode accessCode = new ClientSessionCode(realm, clientSession);
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
index 2adf26c..01bdb61 100755
--- a/services/src/main/java/org/keycloak/services/resources/flows/Flows.java
+++ b/services/src/main/java/org/keycloak/services/resources/flows/Flows.java
@@ -21,14 +21,11 @@
*/
package org.keycloak.services.resources.flows;
-import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.ClientConnection;
import org.keycloak.login.LoginFormsProvider;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
-import org.keycloak.services.managers.AuthenticationManager;
-import org.keycloak.services.managers.TokenManager;
import org.keycloak.social.SocialProvider;
import javax.ws.rs.core.Response;
@@ -46,11 +43,6 @@ public class Flows {
return session.getProvider(LoginFormsProvider.class).setRealm(realm).setUriInfo(uriInfo).setClient(client);
}
- public static OAuthFlows oauth(KeycloakSession session, RealmModel realm, HttpRequest request, UriInfo uriInfo, ClientConnection clientConnection, AuthenticationManager authManager,
- TokenManager tokenManager) {
- return new OAuthFlows(session, realm, request, uriInfo, clientConnection, authManager, tokenManager);
- }
-
public static SocialRedirectFlows social(RealmModel realm, UriInfo uriInfo, ClientConnection clientConnection, SocialProvider provider) {
return new SocialRedirectFlows(realm, uriInfo, clientConnection, provider);
}
diff --git a/services/src/main/java/org/keycloak/services/resources/RequiredActionsService.java b/services/src/main/java/org/keycloak/services/resources/RequiredActionsService.java
index ca5f72f..5fb47e2 100755
--- a/services/src/main/java/org/keycloak/services/resources/RequiredActionsService.java
+++ b/services/src/main/java/org/keycloak/services/resources/RequiredActionsService.java
@@ -24,8 +24,6 @@ package org.keycloak.services.resources;
import org.jboss.logging.Logger;
import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.ClientConnection;
-import org.keycloak.OAuth2Constants;
-import org.keycloak.events.Event;
import org.keycloak.events.EventBuilder;
import org.keycloak.events.Details;
import org.keycloak.events.Errors;
@@ -42,13 +40,12 @@ import org.keycloak.models.UserModel;
import org.keycloak.models.UserModel.RequiredAction;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.utils.TimeBasedOTP;
+import org.keycloak.protocol.LoginProtocol;
import org.keycloak.representations.idm.CredentialRepresentation;
-import org.keycloak.services.managers.AccessCode;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.services.managers.TokenManager;
import org.keycloak.services.messages.Messages;
-import org.keycloak.services.protocol.OpenIdConnectProtocol;
import org.keycloak.services.resources.flows.Flows;
import org.keycloak.services.resources.flows.Urls;
import org.keycloak.services.validation.Validation;
@@ -165,7 +162,7 @@ public class RequiredActionsService {
String error = Validation.validateUpdateProfileForm(formData);
if (error != null) {
return Flows.forms(session, realm, null, uriInfo).setUser(user).setError(error)
- .setAccessCode(accessCode.getCode())
+ .setClientSessionCode(accessCode.getCode())
.createResponse(RequiredAction.UPDATE_PROFILE);
}
@@ -212,11 +209,11 @@ public class RequiredActionsService {
LoginFormsProvider loginForms = Flows.forms(session, realm, null, uriInfo).setUser(user);
if (Validation.isEmpty(totp)) {
return loginForms.setError(Messages.MISSING_TOTP)
- .setAccessCode(accessCode.getCode())
+ .setClientSessionCode(accessCode.getCode())
.createResponse(RequiredAction.CONFIGURE_TOTP);
} else if (!new TimeBasedOTP().validate(totp, totpSecret.getBytes())) {
return loginForms.setError(Messages.INVALID_TOTP)
- .setAccessCode(accessCode.getCode())
+ .setClientSessionCode(accessCode.getCode())
.createResponse(RequiredAction.CONFIGURE_TOTP);
}
@@ -257,11 +254,11 @@ public class RequiredActionsService {
LoginFormsProvider loginForms = Flows.forms(session, realm, null, uriInfo).setUser(user);
if (Validation.isEmpty(passwordNew)) {
return loginForms.setError(Messages.MISSING_PASSWORD)
- .setAccessCode(accessCode.getCode())
+ .setClientSessionCode(accessCode.getCode())
.createResponse(RequiredAction.UPDATE_PASSWORD);
} else if (!passwordNew.equals(passwordConfirm)) {
return loginForms.setError(Messages.NOTMATCH_PASSWORD)
- .setAccessCode(accessCode.getCode())
+ .setClientSessionCode(accessCode.getCode())
.createResponse(RequiredAction.UPDATE_PASSWORD);
}
@@ -269,7 +266,7 @@ public class RequiredActionsService {
session.users().updateCredential(realm, user, UserCredentialModel.password(passwordNew));
} catch (Exception ape) {
return loginForms.setError(ape.getMessage())
- .setAccessCode(accessCode.getCode())
+ .setClientSessionCode(accessCode.getCode())
.createResponse(RequiredAction.UPDATE_PASSWORD);
}
@@ -330,7 +327,7 @@ public class RequiredActionsService {
initEvent(clientSession);
return Flows.forms(session, realm, null, uriInfo)
- .setAccessCode(accessCode.getCode())
+ .setClientSessionCode(accessCode.getCode())
.setVerifyCode(verifyCode)
.setUser(userSession.getUser())
.createResponse(RequiredAction.VERIFY_EMAIL);
@@ -356,11 +353,11 @@ public class RequiredActionsService {
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Somebody is trying to illegally change your password.");
}
return Flows.forms(session, realm, null, uriInfo)
- .setAccessCode(accessCode.getCode())
+ .setClientSessionCode(accessCode.getCode())
.createResponse(RequiredAction.UPDATE_PASSWORD);
} else {
return Flows.forms(session, realm, null, uriInfo)
- .setAccessCode(code)
+ .setClientSessionCode(code)
.createPasswordReset();
}
}
@@ -433,7 +430,7 @@ public class RequiredActionsService {
} catch (EmailException e) {
logger.error("Failed to send password reset email", e);
return Flows.forms(this.session, realm, client, uriInfo).setError("emailSendError")
- .setAccessCode(accessCode.getCode())
+ .setClientSessionCode(accessCode.getCode())
.createErrorPage();
}
}
@@ -442,36 +439,7 @@ public class RequiredActionsService {
}
private Response redirectOauth(UserModel user, ClientSessionCode accessCode, ClientSessionModel clientSession, UserSessionModel userSession) {
- if (accessCode == null) {
- return null;
- }
-
- Set<RequiredAction> requiredActions = user.getRequiredActions();
- if (!requiredActions.isEmpty()) {
- accessCode.setRequiredAction(requiredActions.iterator().next());
- return Flows.forms(session, realm, null, uriInfo)
- .setAccessCode(accessCode.getCode())
- .setUser(user)
- .createResponse(requiredActions.iterator().next());
- } else {
- logger.debugv("Redirecting to: {0}", clientSession.getRedirectUri());
- accessCode.setAction(ClientSessionModel.Action.CODE_TO_TOKEN);
-
- AuthenticationManager authManager = new AuthenticationManager();
-
- if (!AuthenticationManager.isSessionValid(realm, userSession)) {
- AuthenticationManager.logout(session, realm, userSession, uriInfo, clientConnection);
- return Flows.oauth(this.session, realm, request, uriInfo, clientConnection, authManager, tokenManager)
- .redirectError(clientSession.getClient(), "access_denied", clientSession.getNote(OpenIdConnectProtocol.STATE_PARAM), clientSession.getRedirectUri());
- }
- event.session(userSession);
-
- event.success();
-
- return Flows.oauth(this.session, realm, request, uriInfo, clientConnection, authManager, tokenManager)
- .redirectAccessCode(accessCode,
- userSession, clientSession.getNote(OpenIdConnectProtocol.STATE_PARAM), clientSession.getRedirectUri());
- }
+ return AuthenticationManager.nextActionAfterAuthentication(session, userSession, clientSession, clientConnection, request, uriInfo, event);
}
private void initEvent(ClientSessionModel clientSession) {
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 7a8733a..5c0b039 100755
--- a/services/src/main/java/org/keycloak/services/resources/SocialResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/SocialResource.java
@@ -25,12 +25,10 @@ import org.jboss.logging.Logger;
import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.ClientConnection;
-import org.keycloak.OAuth2Constants;
import org.keycloak.events.EventBuilder;
import org.keycloak.events.Details;
import org.keycloak.events.Errors;
import org.keycloak.events.EventType;
-import org.keycloak.jose.jws.JWSInput;
import org.keycloak.models.AccountRoles;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
@@ -48,7 +46,6 @@ import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.managers.TokenManager;
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.SocialAccessDeniedException;
@@ -63,7 +60,6 @@ import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
-import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
@@ -156,7 +152,7 @@ public class SocialResource {
} catch (SocialAccessDeniedException e) {
event.error(Errors.REJECTED_BY_USER);
clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE);
- return Flows.forms(session, realm, clientSession.getClient(), uriInfo).setAccessCode(clientCode.getCode()).setWarning("Access denied").createLogin();
+ return Flows.forms(session, realm, clientSession.getClient(), uriInfo).setClientSessionCode(clientCode.getCode()).setWarning("Access denied").createLogin();
} catch (SocialProviderException e) {
logger.error("Failed to process social callback", e);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Failed to process social callback");
@@ -230,8 +226,8 @@ public class SocialResource {
event.session(userSession);
TokenManager.attachClientSession(userSession, clientSession);
- OAuthFlows oauth = Flows.oauth(session, realm, request, uriInfo, clientConnection, new AuthenticationManager(), tokenManager);
- Response response = oauth.processAccessCode(clientSession, userSession, event);
+ AuthenticationManager authManager = new AuthenticationManager();
+ Response response = authManager.nextActionAfterAuthentication(session, userSession, clientSession, clientConnection, request, uriInfo, event);
if (session.getTransaction().isActive()) {
session.getTransaction().commit();
}
@@ -239,7 +235,7 @@ public class SocialResource {
} catch (ModelDuplicateException e) {
// Assume email is the duplicate as there's nothing else atm
return Flows.forms(session, realm, clientSession.getClient(), uriInfo)
- .setAccessCode(clientCode.getCode())
+ .setClientSessionCode(clientCode.getCode())
.setError("socialEmailExists")
.createLogin();
}
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 55a320c..17ec212 100755
--- a/services/src/main/java/org/keycloak/services/resources/TokenService.java
+++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java
@@ -31,6 +31,7 @@ import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.protocol.LoginProtocol;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.RefreshToken;
@@ -40,12 +41,10 @@ import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.AuthenticationManager.AuthenticationStatus;
import org.keycloak.representations.PasswordToken;
import org.keycloak.services.managers.ClientSessionCode;
-import org.keycloak.services.managers.ResourceAdminManager;
import org.keycloak.services.managers.TokenManager;
import org.keycloak.services.messages.Messages;
-import org.keycloak.services.protocol.OpenIdConnectProtocol;
import org.keycloak.services.resources.flows.Flows;
-import org.keycloak.services.resources.flows.OAuthFlows;
+import org.keycloak.protocol.oidc.OAuthFlows;
import org.keycloak.services.resources.flows.Urls;
import org.keycloak.services.validation.Validation;
import org.keycloak.util.Base64Url;
@@ -114,8 +113,6 @@ public class TokenService {
protected ResourceContext resourceContext;
*/
- private ResourceAdminManager resourceAdminManager = new ResourceAdminManager();
-
public TokenService(RealmModel realm, TokenManager tokenManager, EventBuilder event, AuthenticationManager authManager) {
this.realm = realm;
this.tokenManager = tokenManager;
@@ -492,7 +489,7 @@ public class TokenService {
clientCode.setAction(ClientSessionModel.Action.AUTHENTICATE);
event.client(clientSession.getClient()).error(Errors.INVALID_USER_CREDENTIALS);
return Flows.forms(this.session, realm, clientSession.getClient(), uriInfo).setError(Messages.INVALID_USER)
- .setAccessCode(clientCode.getCode())
+ .setClientSessionCode(clientCode.getCode())
.createLogin();
}
@@ -511,7 +508,6 @@ public class TokenService {
event.detail(Details.REMEMBER_ME, "true");
}
- OAuthFlows oauth = Flows.oauth(session, realm, request, uriInfo, clientConnection, authManager, tokenManager);
ClientModel client = clientSession.getClient();
if (client == null) {
@@ -525,7 +521,12 @@ public class TokenService {
if (formData.containsKey("cancel")) {
event.error(Errors.REJECTED_BY_USER);
- return oauth.redirectError(client, "access_denied", clientSession.getNote(OpenIdConnectProtocol.STATE_PARAM), clientSession.getRedirectUri());
+ LoginProtocol protocol = session.getProvider(LoginProtocol.class, clientSession.getAuthMethod());
+ protocol.setRealm(realm)
+ .setRequest(request)
+ .setUriInfo(uriInfo)
+ .setClientConnection(clientConnection);
+ return protocol.cancelLogin(clientSession);
}
AuthenticationStatus status = authManager.authenticateForm(session, clientConnection, realm, formData);
@@ -547,20 +548,19 @@ public class TokenService {
UserSessionModel userSession = session.sessions().createUserSession(realm, user, username, clientConnection.getRemoteAddr(), "form", remember);
TokenManager.attachClientSession(userSession, clientSession);
event.session(userSession);
-
- return oauth.processAccessCode(clientSession, userSession, event);
+ return authManager.nextActionAfterAuthentication(session, userSession, clientSession, clientConnection, request, uriInfo, event);
case ACCOUNT_TEMPORARILY_DISABLED:
event.error(Errors.USER_TEMPORARILY_DISABLED);
return Flows.forms(this.session, realm, client, uriInfo)
.setError(Messages.ACCOUNT_TEMPORARILY_DISABLED)
.setFormData(formData)
- .setAccessCode(clientCode.getCode())
+ .setClientSessionCode(clientCode.getCode())
.createLogin();
case ACCOUNT_DISABLED:
event.error(Errors.USER_DISABLED);
return Flows.forms(this.session, realm, client, uriInfo)
.setError(Messages.ACCOUNT_DISABLED)
- .setAccessCode(clientCode.getCode())
+ .setClientSessionCode(clientCode.getCode())
.setFormData(formData).createLogin();
case MISSING_TOTP:
formData.remove(CredentialRepresentation.PASSWORD);
@@ -570,19 +570,19 @@ public class TokenService {
return Flows.forms(this.session, realm, client, uriInfo)
.setFormData(formData)
- .setAccessCode(clientCode.getCode())
+ .setClientSessionCode(clientCode.getCode())
.createLoginTotp();
case INVALID_USER:
event.error(Errors.USER_NOT_FOUND);
return Flows.forms(this.session, realm, client, uriInfo).setError(Messages.INVALID_USER)
.setFormData(formData)
- .setAccessCode(clientCode.getCode())
+ .setClientSessionCode(clientCode.getCode())
.createLogin();
default:
event.error(Errors.INVALID_USER_CREDENTIALS);
return Flows.forms(this.session, realm, client, uriInfo).setError(Messages.INVALID_USER)
.setFormData(formData)
- .setAccessCode(clientCode.getCode())
+ .setClientSessionCode(clientCode.getCode())
.createLogin();
}
}
@@ -642,8 +642,6 @@ public class TokenService {
.detail(Details.EMAIL, email)
.detail(Details.REGISTER_METHOD, "form");
- OAuthFlows oauth = Flows.oauth(session, realm, request, uriInfo, clientConnection, authManager, tokenManager);
-
if (!realm.isEnabled()) {
event.error(Errors.REALM_DISABLED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Realm not enabled");
@@ -676,7 +674,7 @@ public class TokenService {
return Flows.forms(session, realm, client, uriInfo)
.setError(error)
.setFormData(formData)
- .setAccessCode(clientCode.getCode())
+ .setClientSessionCode(clientCode.getCode())
.createRegistration();
}
@@ -686,7 +684,7 @@ public class TokenService {
return Flows.forms(session, realm, client, uriInfo)
.setError(Messages.USERNAME_EXISTS)
.setFormData(formData)
- .setAccessCode(clientCode.getCode())
+ .setClientSessionCode(clientCode.getCode())
.createRegistration();
}
@@ -696,7 +694,7 @@ public class TokenService {
return Flows.forms(session, realm, client, uriInfo)
.setError(Messages.EMAIL_EXISTS)
.setFormData(formData)
- .setAccessCode(clientCode.getCode())
+ .setClientSessionCode(clientCode.getCode())
.createRegistration();
}
@@ -727,7 +725,7 @@ public class TokenService {
user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
return Flows.forms(session, realm, client, uriInfo)
.setError(passwordUpdateError)
- .setAccessCode(clientCode.getCode())
+ .setClientSessionCode(clientCode.getCode())
.createResponse(UserModel.RequiredAction.UPDATE_PASSWORD);
}
}
@@ -981,15 +979,15 @@ public class TokenService {
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid code, please login again through your application.");
}
clientSession = clientCode.getClientSession();
- if (!clientSession.getAuthMethod().equals(OpenIdConnectProtocol.LOGIN_PAGE_PROTOCOL)) {
+ if (!clientSession.getAuthMethod().equals(OAuthFlows.LOGIN_PAGE_PROTOCOL)) {
event.error(Errors.INVALID_CODE);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid protocol, please login again through your application.");
}
- state = clientSession.getNote(OpenIdConnectProtocol.STATE_PARAM);
- scopeParam = clientSession.getNote(OpenIdConnectProtocol.SCOPE_PARAM);
- responseType = clientSession.getNote(OpenIdConnectProtocol.RESPONSE_TYPE_PARAM);
- loginHint = clientSession.getNote(OpenIdConnectProtocol.LOGIN_HINT_PARAM);
- prompt = clientSession.getNote(OpenIdConnectProtocol.PROMPT_PARAM);
+ state = clientSession.getNote(OAuthFlows.STATE_PARAM);
+ scopeParam = clientSession.getNote(OAuthFlows.SCOPE_PARAM);
+ responseType = clientSession.getNote(OAuthFlows.RESPONSE_TYPE_PARAM);
+ loginHint = clientSession.getNote(OAuthFlows.LOGIN_HINT_PARAM);
+ prompt = clientSession.getNote(OAuthFlows.PROMPT_PARAM);
} else {
if (state == null) {
event.error(Errors.STATE_PARAM_NOT_FOUND);
@@ -1020,14 +1018,14 @@ public class TokenService {
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid redirect_uri.");
}
clientSession = session.sessions().createClientSession(realm, client);
- clientSession.setAuthMethod(OpenIdConnectProtocol.LOGIN_PAGE_PROTOCOL);
+ clientSession.setAuthMethod(OAuthFlows.LOGIN_PAGE_PROTOCOL);
clientSession.setRedirectUri(redirect);
clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE);
- clientSession.setNote(OpenIdConnectProtocol.STATE_PARAM, state);
- if (scopeParam != null) clientSession.setNote(OpenIdConnectProtocol.SCOPE_PARAM, scopeParam);
- if (responseType != null) clientSession.setNote(OpenIdConnectProtocol.RESPONSE_TYPE_PARAM, responseType);
- if (loginHint != null) clientSession.setNote(OpenIdConnectProtocol.LOGIN_HINT_PARAM, loginHint);
- if (prompt != null) clientSession.setNote(OpenIdConnectProtocol.PROMPT_PARAM, prompt);
+ clientSession.setNote(OAuthFlows.STATE_PARAM, state);
+ if (scopeParam != null) clientSession.setNote(OAuthFlows.SCOPE_PARAM, scopeParam);
+ if (responseType != null) clientSession.setNote(OAuthFlows.RESPONSE_TYPE_PARAM, responseType);
+ if (loginHint != null) clientSession.setNote(OAuthFlows.LOGIN_HINT_PARAM, loginHint);
+ if (prompt != null) clientSession.setNote(OAuthFlows.PROMPT_PARAM, prompt);
}
return null;
}
@@ -1051,13 +1049,13 @@ public class TokenService {
@Path("login")
@GET
public Response loginPage(@QueryParam("code") String code,
- @QueryParam(OpenIdConnectProtocol.RESPONSE_TYPE_PARAM) String responseType,
- @QueryParam(OpenIdConnectProtocol.REDIRECT_URI_PARAM) String redirect,
- @QueryParam(OpenIdConnectProtocol.CLIENT_ID_PARAM) String clientId,
- @QueryParam(OpenIdConnectProtocol.SCOPE_PARAM) String scopeParam,
- @QueryParam(OpenIdConnectProtocol.STATE_PARAM) String state,
- @QueryParam(OpenIdConnectProtocol.PROMPT_PARAM) String prompt,
- @QueryParam(OpenIdConnectProtocol.LOGIN_HINT_PARAM) String loginHint) {
+ @QueryParam(OAuthFlows.RESPONSE_TYPE_PARAM) String responseType,
+ @QueryParam(OAuthFlows.REDIRECT_URI_PARAM) String redirect,
+ @QueryParam(OAuthFlows.CLIENT_ID_PARAM) String clientId,
+ @QueryParam(OAuthFlows.SCOPE_PARAM) String scopeParam,
+ @QueryParam(OAuthFlows.STATE_PARAM) String state,
+ @QueryParam(OAuthFlows.PROMPT_PARAM) String prompt,
+ @QueryParam(OAuthFlows.LOGIN_HINT_PARAM) String loginHint) {
event.event(EventType.LOGIN);
FrontPageInitializer pageInitializer = new FrontPageInitializer();
pageInitializer.code = code;
@@ -1081,23 +1079,23 @@ public class TokenService {
loginHint = pageInitializer.loginHint;
- OAuthFlows oauth = Flows.oauth(session, realm, request, uriInfo, clientConnection, authManager, tokenManager);
AuthenticationManager.AuthResult authResult = authManager.authenticateIdentityCookie(session, realm, uriInfo, clientConnection, headers);
if (authResult != null) {
UserModel user = authResult.getUser();
- UserSessionModel session = authResult.getSession();
- TokenManager.attachClientSession(session, clientSession);
- event.user(user).session(session).detail(Details.AUTH_METHOD, "sso");
- return oauth.processAccessCode(clientSession, session, event);
+ UserSessionModel userSession = authResult.getSession();
+ TokenManager.attachClientSession(userSession, clientSession);
+ event.user(user).session(userSession).detail(Details.AUTH_METHOD, "sso");
+ return authManager.nextActionAfterAuthentication(session, userSession, clientSession, clientConnection, request, uriInfo, event);
}
if (prompt != null && prompt.equals("none")) {
- return oauth.redirectError(clientSession.getClient(), "access_denied", state, redirect);
+ OAuthFlows oauth = new OAuthFlows(session, realm, request, uriInfo, clientConnection);
+ return oauth.cancelLogin(clientSession);
}
LoginFormsProvider forms = Flows.forms(session, realm, clientSession.getClient(), uriInfo)
- .setAccessCode(new ClientSessionCode(realm, clientSession).getCode());
+ .setClientSessionCode(new ClientSessionCode(realm, clientSession).getCode());
String rememberMeUsername = null;
if (realm.isRememberMe()) {
@@ -1136,11 +1134,11 @@ public class TokenService {
@Path("registrations")
@GET
public Response registerPage(@QueryParam("code") String code,
- @QueryParam("response_type") String responseType,
- @QueryParam(OpenIdConnectProtocol.REDIRECT_URI_PARAM) String redirect,
- @QueryParam(OpenIdConnectProtocol.CLIENT_ID_PARAM) String clientId,
- @QueryParam("scope") String scopeParam,
- @QueryParam("state") String state) {
+ @QueryParam(OAuthFlows.RESPONSE_TYPE_PARAM) String responseType,
+ @QueryParam(OAuthFlows.REDIRECT_URI_PARAM) String redirect,
+ @QueryParam(OAuthFlows.CLIENT_ID_PARAM) String clientId,
+ @QueryParam(OAuthFlows.SCOPE_PARAM) String scopeParam,
+ @QueryParam(OAuthFlows.STATE_PARAM) String state) {
event.event(EventType.REGISTER);
if (!realm.isRegistrationAllowed()) {
event.error(Errors.REGISTRATION_DISABLED);
@@ -1162,7 +1160,7 @@ public class TokenService {
authManager.expireIdentityCookie(realm, uriInfo, clientConnection);
return Flows.forms(session, realm, clientSession.getClient(), uriInfo)
- .setAccessCode(new ClientSessionCode(realm, clientSession).getCode())
+ .setClientSessionCode(new ClientSessionCode(realm, clientSession).getCode())
.createRegistration();
}
@@ -1175,7 +1173,7 @@ public class TokenService {
@Path("logout")
@GET
@NoCache
- public Response logout(final @QueryParam(OpenIdConnectProtocol.REDIRECT_URI_PARAM) String redirectUri) {
+ public Response logout(final @QueryParam(OAuthFlows.REDIRECT_URI_PARAM) String redirectUri) {
event.event(EventType.LOGOUT);
if (redirectUri != null) {
event.detail(Details.REDIRECT_URI, redirectUri);
@@ -1189,7 +1187,6 @@ public class TokenService {
if (redirectUri != null) {
String validatedRedirect = verifyRealmRedirectUri(uriInfo, redirectUri, realm);
if (validatedRedirect == null) {
- OAuthFlows oauth = Flows.oauth(session, realm, request, uriInfo, clientConnection, authManager, tokenManager);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid redirect uri.");
}
return Response.status(302).location(UriBuilder.fromUri(validatedRedirect).build()).build();
@@ -1267,7 +1264,6 @@ public class TokenService {
public Response processOAuth(final MultivaluedMap<String, String> formData) {
event.event(EventType.LOGIN).detail(Details.RESPONSE_TYPE, "code");
- OAuthFlows oauth = Flows.oauth(session, realm, request, uriInfo, clientConnection, authManager, tokenManager);
if (!checkSsl()) {
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "HTTPS required");
@@ -1306,15 +1302,19 @@ public class TokenService {
}
event.session(userSession);
+ LoginProtocol protocol = session.getProvider(LoginProtocol.class, clientSession.getAuthMethod());
+ protocol.setRealm(realm)
+ .setRequest(request)
+ .setUriInfo(uriInfo)
+ .setClientConnection(clientConnection);
if (formData.containsKey("cancel")) {
event.error(Errors.REJECTED_BY_USER);
- return redirectAccessDenied(redirect, clientSession.getNote(OpenIdConnectProtocol.STATE_PARAM));
+ return protocol.consentDenied(clientSession);
}
event.success();
- accessCode.setAction(ClientSessionModel.Action.CODE_TO_TOKEN);
- return oauth.redirectAccessCode(accessCode, userSession, clientSession.getNote(OpenIdConnectProtocol.STATE_PARAM), redirect);
+ return authManager.redirectAfterSuccessfulFlow(session, realm, userSession, clientSession, request, uriInfo, clientConnection);
}
@Path("oauth/oob")
@@ -1322,20 +1322,12 @@ public class TokenService {
public Response installedAppUrnCallback(final @QueryParam("code") String code, final @QueryParam("error") String error, final @QueryParam("error_description") String errorDescription) {
LoginFormsProvider forms = Flows.forms(session, realm, null, uriInfo);
if (code != null) {
- return forms.setAccessCode(code).createCode();
+ return forms.setClientSessionCode(code).createCode();
} else {
return forms.setError(error).createCode();
}
}
- protected Response redirectAccessDenied(String redirect, String state) {
- UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam(OAuth2Constants.ERROR, "access_denied");
- if (state != null)
- redirectUri.queryParam(OAuth2Constants.STATE, state);
- Response.ResponseBuilder location = Response.status(302).location(redirectUri.build());
- return location.build();
- }
-
public static boolean matchesRedirects(Set<String> validRedirects, String redirect) {
for (String validRedirect : validRedirects) {
if (validRedirect.endsWith("*")) {
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.protocol.LoginProtocolFactory b/services/src/main/resources/META-INF/services/org.keycloak.protocol.LoginProtocolFactory
new file mode 100755
index 0000000..ca8ba63
--- /dev/null
+++ b/services/src/main/resources/META-INF/services/org.keycloak.protocol.LoginProtocolFactory
@@ -0,0 +1 @@
+org.keycloak.protocol.oidc.OpenIDConnectFactory
\ No newline at end of file
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
new file mode 100755
index 0000000..62aa8e7
--- /dev/null
+++ b/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
@@ -0,0 +1 @@
+org.keycloak.protocol.LoginProtocolSpi
\ No newline at end of file
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java
index 9cda36b..5d4dce0 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java
@@ -10,7 +10,7 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
-import org.keycloak.services.protocol.OpenIdConnectProtocol;
+import org.keycloak.protocol.oidc.OAuthFlows;
import org.keycloak.testsuite.rule.KeycloakRule;
import org.keycloak.util.Time;
@@ -74,7 +74,7 @@ public class UserSessionProviderTest {
assertEquals(realm.findClient("test-app").getClientId(), session.getClient().getClientId());
assertEquals(sessions[0].getId(), session.getUserSession().getId());
assertEquals("http://redirect", session.getRedirectUri());
- assertEquals("state", session.getNote(OpenIdConnectProtocol.STATE_PARAM));
+ assertEquals("state", session.getNote(OAuthFlows.STATE_PARAM));
assertEquals(2, session.getRoles().size());
assertTrue(session.getRoles().contains("one"));
assertTrue(session.getRoles().contains("two"));
@@ -250,7 +250,7 @@ public class UserSessionProviderTest {
clientSession.setUserSession(userSession);
clientSession.setRedirectUri("http://redirect");
clientSession.setRoles(new HashSet<String>());
- clientSession.setNote(OpenIdConnectProtocol.STATE_PARAM, "state");
+ clientSession.setNote(OAuthFlows.STATE_PARAM, "state");
}
resetSession();
@@ -289,7 +289,7 @@ public class UserSessionProviderTest {
ClientSessionModel clientSession = session.sessions().createClientSession(realm, client);
if (userSession != null) clientSession.setUserSession(userSession);
clientSession.setRedirectUri(redirect);
- if (state != null) clientSession.setNote(OpenIdConnectProtocol.STATE_PARAM, state);
+ if (state != null) clientSession.setNote(OAuthFlows.STATE_PARAM, state);
if (roles != null) clientSession.setRoles(roles);
return clientSession;
}