Merge pull request #712 from patriot1burke/master
login refactor phase 2, still more to do
This commit is contained in:
commit
6212403569
20 changed files with 498 additions and 443 deletions
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -14,11 +14,6 @@
|
|||
<description/>
|
||||
|
||||
<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>
|
||||
|
|
37
services/src/main/java/org/keycloak/protocol/LoginProtocol.java
Executable file
37
services/src/main/java/org/keycloak/protocol/LoginProtocol.java
Executable file
|
@ -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);
|
||||
}
|
10
services/src/main/java/org/keycloak/protocol/LoginProtocolFactory.java
Executable file
10
services/src/main/java/org/keycloak/protocol/LoginProtocolFactory.java
Executable file
|
@ -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> {
|
||||
}
|
27
services/src/main/java/org/keycloak/protocol/LoginProtocolSpi.java
Executable file
27
services/src/main/java/org/keycloak/protocol/LoginProtocolSpi.java
Executable file
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
178
services/src/main/java/org/keycloak/protocol/oidc/OAuthFlows.java
Executable file
178
services/src/main/java/org/keycloak/protocol/oidc/OAuthFlows.java
Executable file
|
@ -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() {
|
||||
|
||||
}
|
||||
}
|
32
services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnectFactory.java
Executable file
32
services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnectFactory.java
Executable file
|
@ -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";
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
package org.keycloak.services.protocol;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class OpenIdConnectProtocol {
|
||||
public static final String LOGIN_PAGE_PROTOCOL = "oidc_login_page";
|
||||
|
||||
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";
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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("*")) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -1,276 +0,0 @@
|
|||
/*
|
||||
* JBoss, Home of Professional Open Source.
|
||||
* Copyright 2012, Red Hat, Inc., and individual contributors
|
||||
* as indicated by the @author tags. See the copyright.txt file in the
|
||||
* distribution for a full listing of individual contributors.
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2.1 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this software; if not, write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
|
||||
*/
|
||||
package org.keycloak.services.resources.flows;
|
||||
|
||||
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.AccessCode;
|
||||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
import org.keycloak.services.managers.ClientSessionCode;
|
||||
import org.keycloak.services.managers.TokenManager;
|
||||
import org.keycloak.services.protocol.OpenIdConnectProtocol;
|
||||
|
||||
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.HashSet;
|
||||
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 {
|
||||
|
||||
private static final Logger log = Logger.getLogger(OAuthFlows.class);
|
||||
|
||||
private final KeycloakSession session;
|
||||
|
||||
private final RealmModel realm;
|
||||
|
||||
private final HttpRequest request;
|
||||
|
||||
private final UriInfo uriInfo;
|
||||
|
||||
private ClientConnection clientConnection;
|
||||
private final AuthenticationManager authManager;
|
||||
|
||||
private final TokenManager tokenManager;
|
||||
|
||||
OAuthFlows(KeycloakSession session, RealmModel realm, HttpRequest request, UriInfo uriInfo, ClientConnection clientConnection, AuthenticationManager authManager,
|
||||
TokenManager tokenManager) {
|
||||
this.session = session;
|
||||
this.realm = realm;
|
||||
this.request = request;
|
||||
this.uriInfo = uriInfo;
|
||||
this.clientConnection = clientConnection;
|
||||
this.authManager = authManager;
|
||||
this.tokenManager = tokenManager;
|
||||
}
|
||||
|
||||
public Response redirectAccessCode(ClientSessionCode accessCode, UserSessionModel userSession, String state, String redirect) {
|
||||
String code = accessCode.getCode();
|
||||
UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam(OAuth2Constants.CODE, code);
|
||||
log.debugv("redirectAccessCode: state: {0}", state);
|
||||
if (state != null)
|
||||
redirectUri.queryParam(OAuth2Constants.STATE, state);
|
||||
Response.ResponseBuilder location = Response.status(302).location(redirectUri.build());
|
||||
|
||||
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) {
|
||||
log.debugv("Removing old user session: session: {0}", oldSessionId);
|
||||
session.sessions().removeUserSession(realm, oldSession);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// refresh the cookies!
|
||||
authManager.createLoginCookie(realm, userSession.getUser(), userSession, uriInfo, clientConnection);
|
||||
if (userSession.isRememberMe()) authManager.createRememberMeCookie(realm, userSession.getUser().getUsername(), uriInfo, clientConnection);
|
||||
return location.build();
|
||||
}
|
||||
|
||||
public Response redirectError(ClientModel client, String error, String state, String redirect) {
|
||||
UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam(OAuth2Constants.ERROR, error);
|
||||
if (state != null) {
|
||||
redirectUri.queryParam(OAuth2Constants.STATE, state);
|
||||
}
|
||||
return Response.status(302).location(redirectUri.build()).build();
|
||||
}
|
||||
|
||||
|
||||
public Response processAccessCode(ClientSessionModel clientSession, UserSessionModel session, EventBuilder event) {
|
||||
UserModel user = session.getUser();
|
||||
isTotpConfigurationRequired(user);
|
||||
isEmailVerificationRequired(user);
|
||||
ClientModel client = clientSession.getClient();
|
||||
|
||||
boolean isResource = client instanceof ApplicationModel;
|
||||
ClientSessionCode accessCode = new ClientSessionCode(realm, clientSession);
|
||||
|
||||
|
||||
log.debugv("processAccessCode: isResource: {0}", isResource);
|
||||
log.debugv("processAccessCode: go to oauth page?: {0}",
|
||||
!isResource);
|
||||
|
||||
event.detail(Details.CODE_ID, clientSession.getId());
|
||||
|
||||
Set<RequiredAction> requiredActions = user.getRequiredActions();
|
||||
if (!requiredActions.isEmpty()) {
|
||||
RequiredAction action = user.getRequiredActions().iterator().next();
|
||||
accessCode.setRequiredAction(action);
|
||||
|
||||
LoginFormsProvider loginFormsProvider = Flows.forms(this.session, realm, client, uriInfo).setAccessCode(accessCode.getCode()).setUser(user);
|
||||
if (action.equals(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(this.session, realm, client, uriInfo)
|
||||
.setAccessCode(accessCode.getCode())
|
||||
.setAccessRequest(realmRoles, resourceRoles)
|
||||
.setClient(client)
|
||||
.createOAuthGrant();
|
||||
}
|
||||
|
||||
String redirect = clientSession.getRedirectUri();
|
||||
|
||||
if (redirect != null) {
|
||||
event.success();
|
||||
String state = clientSession.getNote(OpenIdConnectProtocol.STATE_PARAM);
|
||||
accessCode.setAction(ClientSessionModel.Action.CODE_TO_TOKEN);
|
||||
return redirectAccessCode(accessCode, session, state, redirect);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
/*
|
||||
public Response processAccessCode(String scopeParam, String state, String redirect, ClientModel client, UserModel user, UserSessionModel session, EventBuilder event) {
|
||||
isTotpConfigurationRequired(user);
|
||||
isEmailVerificationRequired(user);
|
||||
|
||||
boolean isResource = client instanceof ApplicationModel;
|
||||
AccessCode accessCode = tokenManager.createAccessCode(scopeParam, state, redirect, this.session, realm, client, user, session);
|
||||
|
||||
log.debugv("processAccessCode: isResource: {0}", isResource);
|
||||
log.debugv("processAccessCode: go to oauth page?: {0}",
|
||||
!isResource);
|
||||
|
||||
event.detail(Details.CODE_ID, accessCode.getCodeId());
|
||||
|
||||
Set<RequiredAction> requiredActions = user.getRequiredActions();
|
||||
if (!requiredActions.isEmpty()) {
|
||||
RequiredAction action = user.getRequiredActions().iterator().next();
|
||||
accessCode.setRequiredAction(action);
|
||||
|
||||
if (action.equals(RequiredAction.VERIFY_EMAIL)) {
|
||||
event.clone().event(EventType.SEND_VERIFY_EMAIL).detail(Details.EMAIL, accessCode.getUser().getEmail()).success();
|
||||
}
|
||||
|
||||
return Flows.forms(this.session, realm, client, uriInfo).setAccessCode(accessCode.getCode()).setUser(user)
|
||||
.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(this.session, realm, client, uriInfo)
|
||||
.setAccessCode(accessCode.getCode())
|
||||
.setAccessRequest(realmRoles, resourceRoles)
|
||||
.setClient(client)
|
||||
.createOAuthGrant();
|
||||
}
|
||||
|
||||
if (redirect != null) {
|
||||
event.success();
|
||||
|
||||
accessCode.setAction(ClientSessionModel.Action.CODE_TO_TOKEN);
|
||||
return redirectAccessCode(accessCode, session, state, redirect);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
public Response forwardToSecurityFailure(String message) {
|
||||
return Flows.forms(session, realm, null, uriInfo).setError(message).createErrorPage();
|
||||
}
|
||||
*/
|
||||
|
||||
private void isTotpConfigurationRequired(UserModel user) {
|
||||
for (RequiredCredentialModel c : realm.getRequiredCredentials()) {
|
||||
if (c.getType().equals(CredentialRepresentation.TOTP) && !user.isTotp()) {
|
||||
user.addRequiredAction(RequiredAction.CONFIGURE_TOTP);
|
||||
log.debug("User is required to configure totp");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void isEmailVerificationRequired(UserModel user) {
|
||||
if (realm.isVerifyEmail() && !user.isEmailVerified()) {
|
||||
user.addRequiredAction(RequiredAction.VERIFY_EMAIL);
|
||||
log.debug("User is required to verify email");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
org.keycloak.protocol.oidc.OpenIDConnectFactory
|
1
services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
Executable file
1
services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
Executable file
|
@ -0,0 +1 @@
|
|||
org.keycloak.protocol.LoginProtocolSpi
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue