KEYCLOAK-5797 Refactoring authenticationSessions to support login in multiple browser tabs with different clients
This commit is contained in:
1 parent
b466f4d0b6
commit
7b03eed9c8
48 files changed
+844
-550
No files matched your search
@@ -56,6 +56,7 @@ import org.keycloak.services.util.CacheControlUtil;
|
||||
import org.keycloak.services.util.AuthenticationFlowURLHelper;
|
||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||
import org.keycloak.sessions.CommonClientSessionModel;
|
||||
import org.keycloak.sessions.RootAuthenticationSessionModel;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
@@ -222,7 +223,7 @@ public class AuthenticationProcessor {
|
||||
|
||||
public String generateCode() {
|
||||
ClientSessionCode accessCode = new ClientSessionCode(session, getRealm(), getAuthenticationSession());
|
||||
authenticationSession.setTimestamp(Time.currentTime());
|
||||
authenticationSession.getParentSession().setTimestamp(Time.currentTime());
|
||||
return accessCode.getOrGenerateCode();
|
||||
}
|
||||
|
||||
@@ -632,7 +633,10 @@ public class AuthenticationProcessor {
|
||||
|
||||
} else if (e.getError() == AuthenticationFlowError.FORK_FLOW) {
|
||||
ForkFlowException reset = (ForkFlowException)e;
|
||||
AuthenticationSessionModel clone = clone(session, authenticationSession);
|
||||
|
||||
RootAuthenticationSessionModel rootClone = clone(session, authenticationSession.getClient(), authenticationSession.getParentSession());
|
||||
AuthenticationSessionModel clone = rootClone.getAuthenticationSession(authenticationSession.getClient());
|
||||
|
||||
clone.setAction(AuthenticationSessionModel.Action.AUTHENTICATE.name());
|
||||
setAuthenticationSession(clone);
|
||||
|
||||
@@ -748,7 +752,7 @@ public class AuthenticationProcessor {
|
||||
|
||||
public static void resetFlow(AuthenticationSessionModel authSession, String flowPath) {
|
||||
logger.debug("RESET FLOW");
|
||||
authSession.setTimestamp(Time.currentTime());
|
||||
authSession.getParentSession().setTimestamp(Time.currentTime());
|
||||
authSession.setAuthenticatedUser(null);
|
||||
authSession.clearExecutionStatus();
|
||||
authSession.clearUserSessionNotes();
|
||||
@@ -759,20 +763,26 @@ public class AuthenticationProcessor {
|
||||
authSession.setAuthNote(CURRENT_FLOW_PATH, flowPath);
|
||||
}
|
||||
|
||||
public static AuthenticationSessionModel clone(KeycloakSession session, AuthenticationSessionModel authSession) {
|
||||
AuthenticationSessionModel clone = new AuthenticationSessionManager(session).createAuthenticationSession(authSession.getRealm(), authSession.getClient(), true);
|
||||
public static RootAuthenticationSessionModel clone(KeycloakSession session, ClientModel client, RootAuthenticationSessionModel authSession) {
|
||||
RootAuthenticationSessionModel clone = new AuthenticationSessionManager(session).createAuthenticationSession(authSession.getRealm(), true);
|
||||
|
||||
// Transfer just the client "notes", but not "authNotes"
|
||||
for (Map.Entry<String, String> entry : authSession.getClientNotes().entrySet()) {
|
||||
clone.setClientNote(entry.getKey(), entry.getValue());
|
||||
for (Map.Entry<String, AuthenticationSessionModel> entry : authSession.getAuthenticationSessions().entrySet()) {
|
||||
AuthenticationSessionModel asmOrig = entry.getValue();
|
||||
AuthenticationSessionModel asmClone = clone.createAuthenticationSession(asmOrig.getClient());
|
||||
|
||||
asmClone.setRedirectUri(asmOrig.getRedirectUri());
|
||||
asmClone.setProtocol(asmOrig.getProtocol());
|
||||
|
||||
for (Map.Entry<String, String> clientNote : asmOrig.getClientNotes().entrySet()) {
|
||||
asmClone.setClientNote(clientNote.getKey(), clientNote.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
clone.setRedirectUri(authSession.getRedirectUri());
|
||||
clone.setProtocol(authSession.getProtocol());
|
||||
clone.setTimestamp(Time.currentTime());
|
||||
|
||||
clone.setAuthNote(FORKED_FROM, authSession.getId());
|
||||
logger.debugf("Forked authSession %s from authSession %s", clone.getId(), authSession.getId());
|
||||
clone.getAuthenticationSession(client).setAuthNote(FORKED_FROM, authSession.getId());
|
||||
logger.debugf("Forked authSession %s from authSession %s . Client: '%s'", clone.getId(), authSession.getId(), client.getClientId());
|
||||
|
||||
return clone;
|
||||
|
||||
@@ -825,7 +835,8 @@ public class AuthenticationProcessor {
|
||||
if (!code.isActionActive(ClientSessionCode.ActionType.LOGIN)) {
|
||||
throw new AuthenticationFlowException(AuthenticationFlowError.EXPIRED_CODE);
|
||||
}
|
||||
authenticationSession.setTimestamp(Time.currentTime());
|
||||
|
||||
authenticationSession.getParentSession().setTimestamp(Time.currentTime());
|
||||
}
|
||||
|
||||
public Response authenticateOnly() throws AuthenticationFlowException {
|
||||
@@ -872,9 +883,9 @@ public class AuthenticationProcessor {
|
||||
|
||||
if (userSession == null) { // if no authenticator attached a usersession
|
||||
|
||||
userSession = session.sessions().getUserSession(realm, authSession.getId());
|
||||
userSession = session.sessions().getUserSession(realm, authSession.getParentSession().getId());
|
||||
if (userSession == null) {
|
||||
userSession = session.sessions().createUserSession(authSession.getId(), realm, authSession.getAuthenticatedUser(), username, connection.getRemoteAddr(), authSession.getProtocol()
|
||||
userSession = session.sessions().createUserSession(authSession.getParentSession().getId(), realm, authSession.getAuthenticatedUser(), username, connection.getRemoteAddr(), authSession.getProtocol()
|
||||
, remember, brokerSessionId, brokerUserId);
|
||||
} else if (userSession.getUser() == null || !AuthenticationManager.isSessionValid(realm, userSession)) {
|
||||
userSession.restartSession(realm, authSession.getAuthenticatedUser(), username, connection.getRemoteAddr(), authSession.getProtocol()
|
||||
@@ -936,7 +947,7 @@ public class AuthenticationProcessor {
|
||||
if (nextRequiredAction != null) {
|
||||
return AuthenticationManager.redirectToRequiredActions(session, realm, authenticationSession, uriInfo, nextRequiredAction);
|
||||
} else {
|
||||
event.detail(Details.CODE_ID, authenticationSession.getId()); // todo This should be set elsewhere. find out why tests fail. Don't know where this is supposed to be set
|
||||
event.detail(Details.CODE_ID, authenticationSession.getParentSession().getId()); // todo This should be set elsewhere. find out why tests fail. Don't know where this is supposed to be set
|
||||
// the user has successfully logged in and we can clear his/her previous login failure attempts.
|
||||
logSuccess();
|
||||
return AuthenticationManager.finishedRequiredActions(session, authenticationSession, userSession, connection, request, uriInfo, event);
|
||||
|
||||
@@ -149,7 +149,7 @@ public class RequiredActionContextResult implements RequiredActionContext {
|
||||
@Override
|
||||
public String generateCode() {
|
||||
ClientSessionCode<AuthenticationSessionModel> accessCode = new ClientSessionCode<>(session, getRealm(), getAuthenticationSession());
|
||||
authenticationSession.setTimestamp(Time.currentTime());
|
||||
authenticationSession.getParentSession().setTimestamp(Time.currentTime());
|
||||
return accessCode.getOrGenerateCode();
|
||||
}
|
||||
|
||||
|
||||
+4
-1
@@ -30,6 +30,7 @@ import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriBuilderException;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import org.jboss.resteasy.spi.HttpRequest;
|
||||
import org.keycloak.sessions.RootAuthenticationSessionModel;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -111,7 +112,9 @@ public class ActionTokenContext<T extends JsonWebToken> {
|
||||
// set up the account service as the endpoint to call.
|
||||
ClientModel client = realm.getClientByClientId(clientId == null ? Constants.ACCOUNT_MANAGEMENT_CLIENT_ID : clientId);
|
||||
|
||||
authSession = new AuthenticationSessionManager(session).createAuthenticationSession(realm, client, true);
|
||||
RootAuthenticationSessionModel rootAuthSession = new AuthenticationSessionManager(session).createAuthenticationSession(realm, true);
|
||||
authSession = rootAuthSession.createAuthenticationSession(client);
|
||||
|
||||
authSession.setAction(AuthenticationSessionModel.Action.AUTHENTICATE.name());
|
||||
authSession.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||
String redirectUri = Urls.accountBase(uriInfo.getBaseUri()).path("/").build(realm.getName()).toString();
|
||||
|
||||
+2
-2
@@ -75,8 +75,8 @@ public class ExecuteActionsActionTokenHandler extends AbstractActionTokenHander<
|
||||
final KeycloakSession session = tokenContext.getSession();
|
||||
if (tokenContext.isAuthenticationSessionFresh()) {
|
||||
// Update the authentication session in the token
|
||||
token.setAuthenticationSessionId(authSession.getId());
|
||||
UriBuilder builder = Urls.actionTokenBuilder(uriInfo.getBaseUri(), token.serialize(session, realm, uriInfo));
|
||||
token.setAuthenticationSessionId(authSession.getParentSession().getId());
|
||||
UriBuilder builder = Urls.actionTokenBuilder(uriInfo.getBaseUri(), token.serialize(session, realm, uriInfo), authSession.getClient().getClientId());
|
||||
String confirmUri = builder.build(realm.getName()).toString();
|
||||
|
||||
return session.getProvider(LoginFormsProvider.class)
|
||||
|
||||
+14
-1
@@ -31,6 +31,7 @@ public class IdpVerifyAccountLinkActionToken extends DefaultActionToken {
|
||||
private static final String JSON_FIELD_IDENTITY_PROVIDER_USERNAME = "idpu";
|
||||
private static final String JSON_FIELD_IDENTITY_PROVIDER_ALIAS = "idpa";
|
||||
private static final String JSON_FIELD_ORIGINAL_AUTHENTICATION_SESSION_ID = "oasid";
|
||||
private static final String JSON_FIELD_ORIGINAL_CLIENT_UUID = "ocid";
|
||||
|
||||
@JsonProperty(value = JSON_FIELD_IDENTITY_PROVIDER_USERNAME)
|
||||
private String identityProviderUsername;
|
||||
@@ -41,9 +42,13 @@ public class IdpVerifyAccountLinkActionToken extends DefaultActionToken {
|
||||
@JsonProperty(value = JSON_FIELD_ORIGINAL_AUTHENTICATION_SESSION_ID)
|
||||
private String originalAuthenticationSessionId;
|
||||
|
||||
public IdpVerifyAccountLinkActionToken(String userId, int absoluteExpirationInSecs, String authenticationSessionId,
|
||||
@JsonProperty(value = JSON_FIELD_ORIGINAL_CLIENT_UUID)
|
||||
private String originalClientUUID;
|
||||
|
||||
public IdpVerifyAccountLinkActionToken(String userId, int absoluteExpirationInSecs, String authenticationSessionId, String clientUUID,
|
||||
String identityProviderUsername, String identityProviderAlias) {
|
||||
super(userId, TOKEN_TYPE, absoluteExpirationInSecs, null, authenticationSessionId);
|
||||
this.originalClientUUID = clientUUID;
|
||||
this.identityProviderUsername = identityProviderUsername;
|
||||
this.identityProviderAlias = identityProviderAlias;
|
||||
}
|
||||
@@ -74,4 +79,12 @@ public class IdpVerifyAccountLinkActionToken extends DefaultActionToken {
|
||||
public void setOriginalAuthenticationSessionId(String originalAuthenticationSessionId) {
|
||||
this.originalAuthenticationSessionId = originalAuthenticationSessionId;
|
||||
}
|
||||
|
||||
public String getOriginalClientUUID() {
|
||||
return originalClientUUID;
|
||||
}
|
||||
|
||||
public void setOriginalClientUUID(String originalClientUUID) {
|
||||
this.originalClientUUID = originalClientUUID;
|
||||
}
|
||||
}
|
||||
+9
-6
@@ -23,6 +23,7 @@ import org.keycloak.authentication.actiontoken.*;
|
||||
import org.keycloak.authentication.authenticators.broker.IdpEmailVerificationAuthenticator;
|
||||
import org.keycloak.events.*;
|
||||
import org.keycloak.forms.login.LoginFormsProvider;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
@@ -31,7 +32,7 @@ import org.keycloak.services.Urls;
|
||||
import org.keycloak.services.managers.AuthenticationSessionManager;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||
import org.keycloak.sessions.AuthenticationSessionProvider;
|
||||
|
||||
import java.util.Collections;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
@@ -76,8 +77,8 @@ public class IdpVerifyAccountLinkActionTokenHandler extends AbstractActionTokenH
|
||||
AuthenticationSessionModel authSession = tokenContext.getAuthenticationSession();
|
||||
if (tokenContext.isAuthenticationSessionFresh()) {
|
||||
token.setOriginalAuthenticationSessionId(token.getAuthenticationSessionId());
|
||||
token.setAuthenticationSessionId(authSession.getId());
|
||||
UriBuilder builder = Urls.actionTokenBuilder(uriInfo.getBaseUri(), token.serialize(session, realm, uriInfo));
|
||||
token.setAuthenticationSessionId(authSession.getParentSession().getId());
|
||||
UriBuilder builder = Urls.actionTokenBuilder(uriInfo.getBaseUri(), token.serialize(session, realm, uriInfo), authSession.getClient().getClientId());
|
||||
String confirmUri = builder.build(realm.getName()).toString();
|
||||
|
||||
return session.getProvider(LoginFormsProvider.class)
|
||||
@@ -94,14 +95,16 @@ public class IdpVerifyAccountLinkActionTokenHandler extends AbstractActionTokenH
|
||||
AuthenticationSessionManager asm = new AuthenticationSessionManager(session);
|
||||
asm.removeAuthenticationSession(realm, authSession, true);
|
||||
|
||||
AuthenticationSessionProvider authSessProvider = session.authenticationSessions();
|
||||
authSession = authSessProvider.getAuthenticationSession(realm, token.getOriginalAuthenticationSessionId());
|
||||
ClientModel originalClient = realm.getClientById(token.getOriginalClientUUID());
|
||||
authSession = asm.getAuthenticationSessionByIdAndClient(realm, token.getOriginalAuthenticationSessionId(), originalClient);
|
||||
|
||||
if (authSession != null) {
|
||||
authSession.setAuthNote(IdpEmailVerificationAuthenticator.VERIFY_ACCOUNT_IDP_USERNAME, token.getIdentityProviderUsername());
|
||||
} else {
|
||||
authSessProvider.updateNonlocalSessionAuthNotes(
|
||||
|
||||
session.authenticationSessions().updateNonlocalSessionAuthNotes(
|
||||
token.getAuthenticationSessionId(),
|
||||
originalClient,
|
||||
Collections.singletonMap(IdpEmailVerificationAuthenticator.VERIFY_ACCOUNT_IDP_USERNAME, token.getIdentityProviderUsername())
|
||||
);
|
||||
}
|
||||
|
||||
+2
-2
@@ -77,8 +77,8 @@ public class VerifyEmailActionTokenHandler extends AbstractActionTokenHander<Ver
|
||||
if (tokenContext.isAuthenticationSessionFresh()) {
|
||||
// Update the authentication session in the token
|
||||
token.setOriginalAuthenticationSessionId(token.getAuthenticationSessionId());
|
||||
token.setAuthenticationSessionId(authSession.getId());
|
||||
UriBuilder builder = Urls.actionTokenBuilder(uriInfo.getBaseUri(), token.serialize(session, realm, uriInfo));
|
||||
token.setAuthenticationSessionId(authSession.getParentSession().getId());
|
||||
UriBuilder builder = Urls.actionTokenBuilder(uriInfo.getBaseUri(), token.serialize(session, realm, uriInfo), authSession.getClient().getClientId());
|
||||
String confirmUri = builder.build(realm.getName()).toString();
|
||||
|
||||
return session.getProvider(LoginFormsProvider.class)
|
||||
|
||||
+3
-4
@@ -123,18 +123,17 @@ public class IdpEmailVerificationAuthenticator extends AbstractIdpAuthenticator
|
||||
.user(existingUser)
|
||||
.detail(Details.USERNAME, existingUser.getUsername())
|
||||
.detail(Details.EMAIL, existingUser.getEmail())
|
||||
.detail(Details.CODE_ID, authSession.getId())
|
||||
.detail(Details.CODE_ID, authSession.getParentSession().getId())
|
||||
.removeDetail(Details.AUTH_METHOD)
|
||||
.removeDetail(Details.AUTH_TYPE);
|
||||
|
||||
IdpVerifyAccountLinkActionToken token = new IdpVerifyAccountLinkActionToken(
|
||||
existingUser.getId(), absoluteExpirationInSecs, authSession.getId(),
|
||||
existingUser.getId(), absoluteExpirationInSecs, authSession.getParentSession().getId(), authSession.getClient().getId(),
|
||||
brokerContext.getUsername(), brokerContext.getIdpConfig().getAlias()
|
||||
);
|
||||
UriBuilder builder = Urls.actionTokenBuilder(uriInfo.getBaseUri(), token.serialize(session, realm, uriInfo));
|
||||
UriBuilder builder = Urls.actionTokenBuilder(uriInfo.getBaseUri(), token.serialize(session, realm, uriInfo), authSession.getClient().getClientId());
|
||||
String link = builder
|
||||
.queryParam(Constants.EXECUTION, context.getExecution().getId())
|
||||
.queryParam(Constants.CLIENT_ID, context.getExecution().getId())
|
||||
.build(realm.getName()).toString();
|
||||
long expirationInMinutes = TimeUnit.SECONDS.toMinutes(validityInSecs);
|
||||
|
||||
|
||||
+2
-2
@@ -89,7 +89,7 @@ public class ResetCredentialEmail implements Authenticator, AuthenticatorFactory
|
||||
int absoluteExpirationInSecs = Time.currentTime() + validityInSecs;
|
||||
|
||||
// We send the secret in the email in a link as a query param.
|
||||
ResetCredentialsActionToken token = new ResetCredentialsActionToken(user.getId(), absoluteExpirationInSecs, authenticationSession.getId());
|
||||
ResetCredentialsActionToken token = new ResetCredentialsActionToken(user.getId(), absoluteExpirationInSecs, authenticationSession.getParentSession().getId());
|
||||
String link = UriBuilder
|
||||
.fromUri(context.getActionTokenUrl(token.serialize(context.getSession(), context.getRealm(), context.getUriInfo())))
|
||||
.build()
|
||||
@@ -101,7 +101,7 @@ public class ResetCredentialEmail implements Authenticator, AuthenticatorFactory
|
||||
event.clone().event(EventType.SEND_RESET_PASSWORD)
|
||||
.user(user)
|
||||
.detail(Details.USERNAME, username)
|
||||
.detail(Details.EMAIL, user.getEmail()).detail(Details.CODE_ID, authenticationSession.getId()).success();
|
||||
.detail(Details.EMAIL, user.getEmail()).detail(Details.CODE_ID, authenticationSession.getParentSession().getId()).success();
|
||||
context.forkWithSuccessMessage(new FormMessage(Messages.EMAIL_SENT));
|
||||
} catch (EmailException e) {
|
||||
event.clone().event(EventType.SEND_RESET_PASSWORD)
|
||||
|
||||
@@ -134,8 +134,8 @@ public class VerifyEmail implements RequiredActionProvider, RequiredActionFactor
|
||||
int validityInSecs = realm.getActionTokenGeneratedByUserLifespan(VerifyEmailActionToken.TOKEN_TYPE);
|
||||
int absoluteExpirationInSecs = Time.currentTime() + validityInSecs;
|
||||
|
||||
VerifyEmailActionToken token = new VerifyEmailActionToken(user.getId(), absoluteExpirationInSecs, authSession.getId(), user.getEmail());
|
||||
UriBuilder builder = Urls.actionTokenBuilder(uriInfo.getBaseUri(), token.serialize(session, realm, uriInfo));
|
||||
VerifyEmailActionToken token = new VerifyEmailActionToken(user.getId(), absoluteExpirationInSecs, authSession.getParentSession().getId(), user.getEmail());
|
||||
UriBuilder builder = Urls.actionTokenBuilder(uriInfo.getBaseUri(), token.serialize(session, realm, uriInfo), authSession.getClient().getClientId());
|
||||
String link = builder.build(realm.getName()).toString();
|
||||
long expirationInMinutes = TimeUnit.SECONDS.toMinutes(validityInSecs);
|
||||
|
||||
|
||||
+2
-1
@@ -236,7 +236,8 @@ public class PolicyEvaluationService {
|
||||
ClientModel clientModel = realm.getClientById(clientId);
|
||||
String id = KeycloakModelUtils.generateId();
|
||||
|
||||
AuthenticationSessionModel authSession = keycloakSession.authenticationSessions().createAuthenticationSession(id, realm, clientModel);
|
||||
AuthenticationSessionModel authSession = keycloakSession.authenticationSessions().createRootAuthenticationSession(id, realm)
|
||||
.createAuthenticationSession(clientModel);
|
||||
authSession.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||
authSession.setAuthenticatedUser(userModel);
|
||||
userSession = keycloakSession.sessions().createUserSession(id, realm, userModel, userModel.getUsername(), "127.0.0.1", "passwd", false, null, null);
|
||||
|
||||
@@ -40,6 +40,7 @@ import org.keycloak.services.resources.LoginActionsService;
|
||||
import org.keycloak.services.util.CacheControlUtil;
|
||||
import org.keycloak.services.util.AuthenticationFlowURLHelper;
|
||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||
import org.keycloak.sessions.RootAuthenticationSessionModel;
|
||||
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
@@ -108,7 +109,7 @@ public abstract class AuthorizationEndpointBase {
|
||||
AuthenticationFlowModel flow = getAuthenticationFlow();
|
||||
String flowId = flow.getId();
|
||||
AuthenticationProcessor processor = createProcessor(authSession, flowId, LoginActionsService.AUTHENTICATE_PATH);
|
||||
event.detail(Details.CODE_ID, authSession.getId());
|
||||
event.detail(Details.CODE_ID, authSession.getParentSession().getId());
|
||||
if (isPassive) {
|
||||
// OIDC prompt == NONE or SAML 2 IsPassive flag
|
||||
// This means that client is just checking if the user is already completely logged in.
|
||||
@@ -168,45 +169,61 @@ public abstract class AuthorizationEndpointBase {
|
||||
protected AuthorizationEndpointChecks getOrCreateAuthenticationSession(ClientModel client, String requestState) {
|
||||
AuthenticationSessionManager manager = new AuthenticationSessionManager(session);
|
||||
String authSessionId = manager.getCurrentAuthenticationSessionId(realm);
|
||||
AuthenticationSessionModel authSession = authSessionId==null ? null : session.authenticationSessions().getAuthenticationSession(realm, authSessionId);
|
||||
RootAuthenticationSessionModel rootAuthSession = authSessionId==null ? null : session.authenticationSessions().getRootAuthenticationSession(realm, authSessionId);
|
||||
AuthenticationSessionModel authSession;
|
||||
|
||||
if (authSession != null) {
|
||||
if (rootAuthSession != null) {
|
||||
|
||||
ClientSessionCode<AuthenticationSessionModel> check = new ClientSessionCode<>(session, realm, authSession);
|
||||
if (!check.isActionActive(ClientSessionCode.ActionType.LOGIN)) {
|
||||
authSession = rootAuthSession.getAuthenticationSession(client);
|
||||
|
||||
logger.debugf("Authentication session '%s' exists, but is expired. Restart existing authentication session", authSession.getId());
|
||||
authSession.restartSession(realm, client);
|
||||
return new AuthorizationEndpointChecks(authSession);
|
||||
if (authSession != null) {
|
||||
ClientSessionCode<AuthenticationSessionModel> check = new ClientSessionCode<>(session, realm, authSession);
|
||||
if (!check.isActionActive(ClientSessionCode.ActionType.LOGIN)) {
|
||||
|
||||
} else if (isNewRequest(authSession, client, requestState)) {
|
||||
// Check if we have lastProcessedExecution note or if some request parameter beside state (eg. prompt, kc_idp_hint) changed. Restart the session just if yes.
|
||||
// Otherwise update just client information from the AuthorizationEndpoint request.
|
||||
// This difference is needed, because of logout from JS applications in multiple browser tabs.
|
||||
if (shouldRestartAuthSession(authSession)) {
|
||||
logger.debug("New request from application received, but authentication session already exists. Restart existing authentication session");
|
||||
authSession.restartSession(realm, client);
|
||||
} else {
|
||||
logger.debug("New request from application received, but authentication session already exists. Update client information in existing authentication session");
|
||||
authSession.clearClientNotes(); // update client data
|
||||
authSession.updateClient(client);
|
||||
}
|
||||
logger.debugf("Authentication session '%s' exists, but is expired. Restart existing authentication session", rootAuthSession.getId());
|
||||
rootAuthSession.restartSession(realm);
|
||||
authSession = rootAuthSession.createAuthenticationSession(client);
|
||||
|
||||
return new AuthorizationEndpointChecks(authSession);
|
||||
|
||||
} else {
|
||||
logger.debug("Re-sent some previous request to Authorization endpoint. Likely browser 'back' or 'refresh' button.");
|
||||
|
||||
// See if we have lastProcessedExecution note. If yes, we are expired. Also if we are in different flow than initial one. Otherwise it is browser refresh of initial username/password form
|
||||
if (!shouldShowExpirePage(authSession)) {
|
||||
return new AuthorizationEndpointChecks(authSession);
|
||||
} else {
|
||||
CacheControlUtil.noBackButtonCacheControlHeader();
|
||||
|
||||
Response response = new AuthenticationFlowURLHelper(session, realm, uriInfo)
|
||||
.showPageExpired(authSession);
|
||||
return new AuthorizationEndpointChecks(response);
|
||||
} else if (isNewRequest(authSession, client, requestState)) {
|
||||
// Check if we have lastProcessedExecution note or if some request parameter beside state (eg. prompt, kc_idp_hint) changed. Restart the session just if yes.
|
||||
// Otherwise update just client information from the AuthorizationEndpoint request.
|
||||
// This difference is needed, because of logout from JS applications in multiple browser tabs.
|
||||
if (shouldRestartAuthSession(authSession)) {
|
||||
logger.debugf("New request from application received, but authentication session '%s' already exists and has client '%s'. Restart child authentication session for client.",
|
||||
rootAuthSession.getId(), client.getClientId());
|
||||
|
||||
authSession = rootAuthSession.createAuthenticationSession(client);
|
||||
|
||||
} else {
|
||||
logger.debugf("New request from application received, but authentication session '%s' already exists and has client '%s'. Update client information in existing authentication session.",
|
||||
rootAuthSession.getId(), client.getClientId());
|
||||
authSession.clearClientNotes();
|
||||
}
|
||||
|
||||
return new AuthorizationEndpointChecks(authSession);
|
||||
|
||||
} else {
|
||||
logger.debug("Re-sent some previous request to Authorization endpoint. Likely browser 'back' or 'refresh' button.");
|
||||
|
||||
// See if we have lastProcessedExecution note. If yes, we are expired. Also if we are in different flow than initial one. Otherwise it is browser refresh of initial username/password form
|
||||
if (!shouldShowExpirePage(authSession)) {
|
||||
return new AuthorizationEndpointChecks(authSession);
|
||||
} else {
|
||||
CacheControlUtil.noBackButtonCacheControlHeader();
|
||||
|
||||
Response response = new AuthenticationFlowURLHelper(session, realm, uriInfo)
|
||||
.showPageExpired(authSession);
|
||||
return new AuthorizationEndpointChecks(response);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.debugf("Sent request to authz endpoint. Authentication session with ID '%s' exists, but doesn't have client: '%s' . Adding client to authentication session",
|
||||
rootAuthSession.getId(), client.getClientId());
|
||||
|
||||
authSession = rootAuthSession.createAuthenticationSession(client);
|
||||
return new AuthorizationEndpointChecks(authSession);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,10 +231,12 @@ public abstract class AuthorizationEndpointBase {
|
||||
|
||||
if (userSession != null) {
|
||||
logger.debugf("Sent request to authz endpoint. We don't have authentication session with ID '%s' but we have userSession. Will re-create authentication session with same ID", authSessionId);
|
||||
authSession = session.authenticationSessions().createAuthenticationSession(authSessionId, realm, client);
|
||||
rootAuthSession = session.authenticationSessions().createRootAuthenticationSession(authSessionId, realm);
|
||||
authSession = rootAuthSession.createAuthenticationSession(client);
|
||||
} else {
|
||||
authSession = manager.createAuthenticationSession(realm, client, true);
|
||||
logger.debugf("Sent request to authz endpoint. Created new authentication session with ID '%s'", authSession.getId());
|
||||
rootAuthSession = manager.createAuthenticationSession(realm, true);
|
||||
authSession = rootAuthSession.createAuthenticationSession(client);
|
||||
logger.debugf("Sent request to authz endpoint. Created new authentication session with ID '%s'", rootAuthSession.getId());
|
||||
}
|
||||
|
||||
return new AuthorizationEndpointChecks(authSession);
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.keycloak.services.managers.AuthenticationManager;
|
||||
import org.keycloak.services.managers.AuthenticationSessionManager;
|
||||
import org.keycloak.services.util.CookieHelper;
|
||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||
import org.keycloak.sessions.RootAuthenticationSessionModel;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.ws.rs.core.Cookie;
|
||||
@@ -143,7 +144,8 @@ public class RestartLoginCookie {
|
||||
}
|
||||
|
||||
|
||||
public static AuthenticationSessionModel restartSession(KeycloakSession session, RealmModel realm) throws Exception {
|
||||
public static AuthenticationSessionModel restartSession(KeycloakSession session, RealmModel realm,
|
||||
RootAuthenticationSessionModel rootSession, String expectedClientId) throws Exception {
|
||||
Cookie cook = session.getContext().getRequestHeaders().getCookies().get(KC_RESTART);
|
||||
if (cook == null) {
|
||||
logger.debug("KC_RESTART cookie doesn't exist");
|
||||
@@ -161,7 +163,18 @@ public class RestartLoginCookie {
|
||||
ClientModel client = realm.getClientByClientId(cookie.getClientId());
|
||||
if (client == null) return null;
|
||||
|
||||
AuthenticationSessionModel authSession = new AuthenticationSessionManager(session).createAuthenticationSession(realm, client, true);
|
||||
// Restart just if client from cookie matches client from the URL.
|
||||
if (!client.getClientId().equals(expectedClientId)) {
|
||||
logger.debugf("Skip restarting from the KC_RESTART. Clients doesn't match: Cookie client: %s, Requested client: %s", client.getClientId(), expectedClientId);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Need to create brand new session and setup cookie
|
||||
if (rootSession == null) {
|
||||
rootSession = new AuthenticationSessionManager(session).createAuthenticationSession(realm, true);
|
||||
}
|
||||
|
||||
AuthenticationSessionModel authSession = rootSession.createAuthenticationSession(client);
|
||||
authSession.setProtocol(cookie.getAuthMethod());
|
||||
authSession.setRedirectUri(cookie.getRedirectUri());
|
||||
authSession.setAction(cookie.getAction());
|
||||
|
||||
@@ -80,6 +80,7 @@ import org.keycloak.services.resources.admin.AdminAuth;
|
||||
import org.keycloak.services.resources.admin.permissions.AdminPermissions;
|
||||
import org.keycloak.services.validation.Validation;
|
||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||
import org.keycloak.sessions.RootAuthenticationSessionModel;
|
||||
import org.keycloak.util.TokenUtil;
|
||||
import org.keycloak.utils.ProfileHelper;
|
||||
|
||||
@@ -255,7 +256,7 @@ public class TokenEndpoint {
|
||||
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_REQUEST, "Missing parameter: " + OAuth2Constants.CODE, Response.Status.BAD_REQUEST);
|
||||
}
|
||||
|
||||
ClientSessionCode.ParseResult<AuthenticatedClientSessionModel> parseResult = ClientSessionCode.parseResult(code, session, realm, event, AuthenticatedClientSessionModel.class);
|
||||
ClientSessionCode.ParseResult<AuthenticatedClientSessionModel> parseResult = ClientSessionCode.parseResult(code, session, realm, client, event, AuthenticatedClientSessionModel.class);
|
||||
if (parseResult.isAuthSessionNotFound() || parseResult.isIllegalHash()) {
|
||||
AuthenticatedClientSessionModel clientSession = parseResult.getClientSession();
|
||||
|
||||
@@ -469,7 +470,9 @@ public class TokenEndpoint {
|
||||
}
|
||||
String scope = formParams.getFirst(OAuth2Constants.SCOPE);
|
||||
|
||||
AuthenticationSessionModel authSession = new AuthenticationSessionManager(session).createAuthenticationSession(realm, client, false);
|
||||
RootAuthenticationSessionModel rootAuthSession = new AuthenticationSessionManager(session).createAuthenticationSession(realm, false);
|
||||
AuthenticationSessionModel authSession = rootAuthSession.createAuthenticationSession(client);
|
||||
|
||||
authSession.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||
authSession.setAction(AuthenticatedClientSessionModel.Action.AUTHENTICATE.name());
|
||||
authSession.setClientNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName()));
|
||||
@@ -553,13 +556,16 @@ public class TokenEndpoint {
|
||||
|
||||
String scope = formParams.getFirst(OAuth2Constants.SCOPE);
|
||||
|
||||
AuthenticationSessionModel authSession = new AuthenticationSessionManager(session).createAuthenticationSession(realm, client, false);
|
||||
RootAuthenticationSessionModel rootAuthSession = new AuthenticationSessionManager(session).createAuthenticationSession(realm, false);
|
||||
AuthenticationSessionModel authSession = rootAuthSession.createAuthenticationSession(client);
|
||||
|
||||
authSession.setAuthenticatedUser(clientUser);
|
||||
authSession.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||
authSession.setClientNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName()));
|
||||
authSession.setClientNote(OIDCLoginProtocol.SCOPE_PARAM, scope);
|
||||
|
||||
UserSessionModel userSession = session.sessions().createUserSession(authSession.getId(), realm, clientUser, clientUsername, clientConnection.getRemoteAddr(), ServiceAccountConstants.CLIENT_AUTH, false, null, null);
|
||||
UserSessionModel userSession = session.sessions().createUserSession(authSession.getParentSession().getId(), realm, clientUser, clientUsername,
|
||||
clientConnection.getRemoteAddr(), ServiceAccountConstants.CLIENT_AUTH, false, null, null);
|
||||
event.session(userSession);
|
||||
|
||||
AuthenticationManager.setRolesAndMappersInSession(authSession);
|
||||
@@ -763,7 +769,9 @@ public class TokenEndpoint {
|
||||
|
||||
String scope = formParams.getFirst(OAuth2Constants.SCOPE);
|
||||
|
||||
AuthenticationSessionModel authSession = new AuthenticationSessionManager(session).createAuthenticationSession(realm, targetClient, false);
|
||||
RootAuthenticationSessionModel rootAuthSession = new AuthenticationSessionManager(session).createAuthenticationSession(realm, false);
|
||||
AuthenticationSessionModel authSession = rootAuthSession.createAuthenticationSession(targetClient);
|
||||
|
||||
authSession.setAuthenticatedUser(targetUser);
|
||||
authSession.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||
authSession.setClientNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName()));
|
||||
|
||||
@@ -182,9 +182,11 @@ public class Urls {
|
||||
return loginResetCredentialsBuilder(baseUri).build(realmName);
|
||||
}
|
||||
|
||||
public static UriBuilder actionTokenBuilder(URI baseUri, String tokenString) {
|
||||
public static UriBuilder actionTokenBuilder(URI baseUri, String tokenString, String clientId) {
|
||||
return loginActionsBase(baseUri).path(LoginActionsService.class, "executeActionToken")
|
||||
.queryParam("key", tokenString);
|
||||
.queryParam("key", tokenString)
|
||||
.queryParam(Constants.CLIENT_ID, clientId);
|
||||
|
||||
}
|
||||
|
||||
public static UriBuilder loginResetCredentialsBuilder(URI baseUri) {
|
||||
|
||||
@@ -56,6 +56,7 @@ import org.keycloak.services.util.CookieHelper;
|
||||
import org.keycloak.services.util.P3PHelper;
|
||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||
import org.keycloak.sessions.CommonClientSessionModel;
|
||||
import org.keycloak.sessions.RootAuthenticationSessionModel;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.ws.rs.core.Cookie;
|
||||
@@ -215,16 +216,20 @@ public class AuthenticationManager {
|
||||
}
|
||||
|
||||
private static AuthenticationSessionModel createOrJoinLogoutSession(RealmModel realm, final AuthenticationSessionManager asm, boolean browserCookie) {
|
||||
// Account management client is used as a placeholder
|
||||
ClientModel client = realm.getClientByClientId(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID);
|
||||
AuthenticationSessionModel logoutAuthSession = asm.getCurrentAuthenticationSession(realm);
|
||||
|
||||
AuthenticationSessionModel logoutAuthSession = asm.getCurrentAuthenticationSession(realm, client);
|
||||
// Try to join existing logout session if it exists and browser session is required
|
||||
if (browserCookie && logoutAuthSession != null) {
|
||||
if (Objects.equals(AuthenticationSessionModel.Action.LOGGING_OUT.name(), logoutAuthSession.getAction())) {
|
||||
return logoutAuthSession;
|
||||
}
|
||||
logoutAuthSession.restartSession(realm, client);
|
||||
// Re-create the authentication session for logout
|
||||
logoutAuthSession = logoutAuthSession.getParentSession().createAuthenticationSession(client);
|
||||
} else {
|
||||
logoutAuthSession = asm.createAuthenticationSession(realm, client, browserCookie);
|
||||
RootAuthenticationSessionModel rootLogoutSession = asm.createAuthenticationSession(realm, browserCookie);
|
||||
logoutAuthSession = rootLogoutSession.createAuthenticationSession(client);
|
||||
}
|
||||
logoutAuthSession.setAction(AuthenticationSessionModel.Action.LOGGING_OUT.name());
|
||||
return logoutAuthSession;
|
||||
@@ -381,8 +386,8 @@ public class AuthenticationManager {
|
||||
/**
|
||||
* Sets logout state of the particular client into the {@code logoutAuthSession}
|
||||
* @param logoutAuthSession logoutAuthSession. May be {@code null} in which case this is a no-op.
|
||||
* @param client Client. Must not be {@code null}
|
||||
* @param state
|
||||
* @param clientUuid Client. Must not be {@code null}
|
||||
* @param action
|
||||
*/
|
||||
public static void setClientLogoutAction(AuthenticationSessionModel logoutAuthSession, String clientUuid, AuthenticationSessionModel.Action action) {
|
||||
if (logoutAuthSession != null && clientUuid != null) {
|
||||
@@ -479,8 +484,11 @@ public class AuthenticationManager {
|
||||
}
|
||||
|
||||
public static Response finishBrowserLogout(KeycloakSession session, RealmModel realm, UserSessionModel userSession, UriInfo uriInfo, ClientConnection connection, HttpHeaders headers) {
|
||||
// Account management client is used as a placeholder
|
||||
ClientModel client = realm.getClientByClientId(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID);
|
||||
|
||||
final AuthenticationSessionManager asm = new AuthenticationSessionManager(session);
|
||||
AuthenticationSessionModel logoutAuthSession = asm.getCurrentAuthenticationSession(realm);
|
||||
AuthenticationSessionModel logoutAuthSession = asm.getCurrentAuthenticationSession(realm, client);
|
||||
checkUserSessionOnlyHasLoggedOutClients(realm, userSession, logoutAuthSession);
|
||||
|
||||
expireIdentityCookie(realm, uriInfo, connection);
|
||||
@@ -832,7 +840,7 @@ public class AuthenticationManager {
|
||||
|
||||
logger.debugv("processAccessCode: go to oauth page?: {0}", client.isConsentRequired());
|
||||
|
||||
event.detail(Details.CODE_ID, authSession.getId());
|
||||
event.detail(Details.CODE_ID, authSession.getParentSession().getId());
|
||||
|
||||
Set<String> requiredActions = user.getRequiredActions();
|
||||
Response action = executionActions(session, authSession, request, event, realm, user, requiredActions);
|
||||
|
||||
+27
-11
@@ -28,6 +28,7 @@ import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.protocol.RestartLoginCookie;
|
||||
import org.keycloak.services.util.CookieHelper;
|
||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||
import org.keycloak.sessions.RootAuthenticationSessionModel;
|
||||
import org.keycloak.sessions.StickySessionEncoderProvider;
|
||||
|
||||
/**
|
||||
@@ -45,22 +46,22 @@ public class AuthenticationSessionManager {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a fresh authentication session for the given realm and client. Optionally sets the browser
|
||||
* Creates a fresh authentication session for the given realm . Optionally sets the browser
|
||||
* authentication session cookie {@link #AUTH_SESSION_ID} with the ID of the new session.
|
||||
* @param realm
|
||||
* @param client
|
||||
* @param browserCookie Set the cookie in the browser for the
|
||||
* @return
|
||||
*/
|
||||
public AuthenticationSessionModel createAuthenticationSession(RealmModel realm, ClientModel client, boolean browserCookie) {
|
||||
AuthenticationSessionModel authSession = session.authenticationSessions().createAuthenticationSession(realm, client);
|
||||
public RootAuthenticationSessionModel createAuthenticationSession(RealmModel realm, boolean browserCookie) {
|
||||
RootAuthenticationSessionModel rootAuthSession = session.authenticationSessions().createRootAuthenticationSession(realm);
|
||||
|
||||
if (browserCookie) {
|
||||
setAuthSessionCookie(authSession.getId(), realm);
|
||||
setAuthSessionCookie(rootAuthSession.getId(), realm);
|
||||
}
|
||||
|
||||
return authSession;
|
||||
return rootAuthSession;
|
||||
}
|
||||
|
||||
|
||||
@@ -73,14 +74,20 @@ public class AuthenticationSessionManager {
|
||||
return getAuthSessionCookieDecoded(realm);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns current authentication session if it exists, otherwise returns {@code null}.
|
||||
* @param realm
|
||||
* @return
|
||||
*/
|
||||
public AuthenticationSessionModel getCurrentAuthenticationSession(RealmModel realm) {
|
||||
public AuthenticationSessionModel getCurrentAuthenticationSession(RealmModel realm, ClientModel client) {
|
||||
String authSessionId = getAuthSessionCookieDecoded(realm);
|
||||
return authSessionId==null ? null : session.authenticationSessions().getAuthenticationSession(realm, authSessionId);
|
||||
|
||||
if (authSessionId == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return getAuthenticationSessionByIdAndClient(realm, authSessionId, client);
|
||||
}
|
||||
|
||||
|
||||
@@ -124,8 +131,10 @@ public class AuthenticationSessionManager {
|
||||
|
||||
|
||||
public void removeAuthenticationSession(RealmModel realm, AuthenticationSessionModel authSession, boolean expireRestartCookie) {
|
||||
log.debugf("Removing authSession '%s'. Expire restart cookie: %b", authSession.getId(), expireRestartCookie);
|
||||
session.authenticationSessions().removeAuthenticationSession(realm, authSession);
|
||||
RootAuthenticationSessionModel rootAuthSession = authSession.getParentSession();
|
||||
|
||||
log.debugf("Removing authSession '%s'. Expire restart cookie: %b", rootAuthSession.getId(), expireRestartCookie);
|
||||
session.authenticationSessions().removeRootAuthenticationSession(realm, rootAuthSession);
|
||||
|
||||
// expire restart cookie
|
||||
if (expireRestartCookie) {
|
||||
@@ -138,7 +147,14 @@ public class AuthenticationSessionManager {
|
||||
|
||||
// Check to see if we already have authenticationSession with same ID
|
||||
public UserSessionModel getUserSession(AuthenticationSessionModel authSession) {
|
||||
return session.sessions().getUserSession(authSession.getRealm(), authSession.getId());
|
||||
return session.sessions().getUserSession(authSession.getRealm(), authSession.getParentSession().getId());
|
||||
}
|
||||
|
||||
|
||||
// Don't look at cookie. Just lookup authentication session based on the ID and client. Return null if not found
|
||||
public AuthenticationSessionModel getAuthenticationSessionByIdAndClient(RealmModel realm, String authSessionId, ClientModel client) {
|
||||
RootAuthenticationSessionModel rootAuthSession = session.authenticationSessions().getRootAuthenticationSession(realm, authSessionId);
|
||||
return rootAuthSession==null ? null : rootAuthSession.getAuthenticationSession(client);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -80,7 +80,8 @@ public class ClientSessionCode<CLIENT_SESSION extends CommonClientSessionModel>
|
||||
}
|
||||
}
|
||||
|
||||
public static <CLIENT_SESSION extends CommonClientSessionModel> ParseResult<CLIENT_SESSION> parseResult(String code, KeycloakSession session, RealmModel realm, EventBuilder event, Class<CLIENT_SESSION> sessionClass) {
|
||||
public static <CLIENT_SESSION extends CommonClientSessionModel> ParseResult<CLIENT_SESSION> parseResult(String code, KeycloakSession session, RealmModel realm, ClientModel client,
|
||||
EventBuilder event, Class<CLIENT_SESSION> sessionClass) {
|
||||
ParseResult<CLIENT_SESSION> result = new ParseResult<>();
|
||||
if (code == null) {
|
||||
result.illegalHash = true;
|
||||
@@ -88,7 +89,7 @@ public class ClientSessionCode<CLIENT_SESSION extends CommonClientSessionModel>
|
||||
}
|
||||
try {
|
||||
CodeGenerateUtil.ClientSessionParser<CLIENT_SESSION> clientSessionParser = CodeGenerateUtil.getParser(sessionClass);
|
||||
result.clientSession = getClientSession(code, session, realm, event, clientSessionParser);
|
||||
result.clientSession = getClientSession(code, session, realm, client, event, clientSessionParser);
|
||||
if (result.clientSession == null) {
|
||||
result.authSessionNotFound = true;
|
||||
return result;
|
||||
@@ -113,15 +114,16 @@ public class ClientSessionCode<CLIENT_SESSION extends CommonClientSessionModel>
|
||||
}
|
||||
|
||||
|
||||
public static <CLIENT_SESSION extends CommonClientSessionModel> CLIENT_SESSION getClientSession(String code, KeycloakSession session, RealmModel realm, EventBuilder event, Class<CLIENT_SESSION> sessionClass) {
|
||||
public static <CLIENT_SESSION extends CommonClientSessionModel> CLIENT_SESSION getClientSession(String code, KeycloakSession session, RealmModel realm, ClientModel client,
|
||||
EventBuilder event, Class<CLIENT_SESSION> sessionClass) {
|
||||
CodeGenerateUtil.ClientSessionParser<CLIENT_SESSION> clientSessionParser = CodeGenerateUtil.getParser(sessionClass);
|
||||
return getClientSession(code, session, realm, event, clientSessionParser);
|
||||
return getClientSession(code, session, realm, client, event, clientSessionParser);
|
||||
}
|
||||
|
||||
|
||||
private static <CLIENT_SESSION extends CommonClientSessionModel> CLIENT_SESSION getClientSession(String code, KeycloakSession session, RealmModel realm, EventBuilder event,
|
||||
private static <CLIENT_SESSION extends CommonClientSessionModel> CLIENT_SESSION getClientSession(String code, KeycloakSession session, RealmModel realm, ClientModel client, EventBuilder event,
|
||||
CodeGenerateUtil.ClientSessionParser<CLIENT_SESSION> clientSessionParser) {
|
||||
return clientSessionParser.parseSession(code, session, realm, event);
|
||||
return clientSessionParser.parseSession(code, session, realm, client, event);
|
||||
}
|
||||
|
||||
|
||||
@@ -135,7 +137,8 @@ public class ClientSessionCode<CLIENT_SESSION extends CommonClientSessionModel>
|
||||
}
|
||||
|
||||
public boolean isActionActive(ActionType actionType) {
|
||||
int timestamp = commonLoginSession.getTimestamp();
|
||||
CodeGenerateUtil.ClientSessionParser<CLIENT_SESSION> clientSessionParser = (CodeGenerateUtil.ClientSessionParser<CLIENT_SESSION>) CodeGenerateUtil.getParser(commonLoginSession.getClass());
|
||||
int timestamp = clientSessionParser.getTimestamp(commonLoginSession);
|
||||
|
||||
int lifespan;
|
||||
switch (actionType) {
|
||||
@@ -210,7 +213,9 @@ public class ClientSessionCode<CLIENT_SESSION extends CommonClientSessionModel>
|
||||
|
||||
public void setAction(String action) {
|
||||
commonLoginSession.setAction(action);
|
||||
commonLoginSession.setTimestamp(Time.currentTime());
|
||||
|
||||
CodeGenerateUtil.ClientSessionParser<CLIENT_SESSION> clientSessionParser = (CodeGenerateUtil.ClientSessionParser<CLIENT_SESSION>) CodeGenerateUtil.getParser(commonLoginSession.getClass());
|
||||
clientSessionParser.setTimestamp(commonLoginSession, Time.currentTime());
|
||||
}
|
||||
|
||||
public String getOrGenerateCode() {
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.keycloak.events.Details;
|
||||
import org.keycloak.events.EventBuilder;
|
||||
import org.keycloak.jose.jwe.JWEException;
|
||||
import org.keycloak.models.AuthenticatedClientSessionModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.CodeToTokenStoreProvider;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
@@ -78,7 +79,7 @@ class CodeGenerateUtil {
|
||||
|
||||
interface ClientSessionParser<CS extends CommonClientSessionModel> {
|
||||
|
||||
CS parseSession(String code, KeycloakSession session, RealmModel realm, EventBuilder event);
|
||||
CS parseSession(String code, KeycloakSession session, RealmModel realm, ClientModel client, EventBuilder event);
|
||||
|
||||
String retrieveCode(KeycloakSession session, CS clientSession);
|
||||
|
||||
@@ -88,6 +89,9 @@ class CodeGenerateUtil {
|
||||
|
||||
boolean isExpired(KeycloakSession session, String code, CS clientSession);
|
||||
|
||||
int getTimestamp(CS clientSession);
|
||||
void setTimestamp(CS clientSession, int timestamp);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -97,9 +101,9 @@ class CodeGenerateUtil {
|
||||
private static class AuthenticationSessionModelParser implements ClientSessionParser<AuthenticationSessionModel> {
|
||||
|
||||
@Override
|
||||
public AuthenticationSessionModel parseSession(String code, KeycloakSession session, RealmModel realm, EventBuilder event) {
|
||||
public AuthenticationSessionModel parseSession(String code, KeycloakSession session, RealmModel realm, ClientModel client, EventBuilder event) {
|
||||
// Read authSessionID from cookie. Code is ignored for now
|
||||
return new AuthenticationSessionManager(session).getCurrentAuthenticationSession(realm);
|
||||
return new AuthenticationSessionManager(session).getCurrentAuthenticationSession(realm, client);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -141,6 +145,16 @@ class CodeGenerateUtil {
|
||||
public boolean isExpired(KeycloakSession session, String code, AuthenticationSessionModel clientSession) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTimestamp(AuthenticationSessionModel clientSession) {
|
||||
return clientSession.getParentSession().getTimestamp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTimestamp(AuthenticationSessionModel clientSession, int timestamp) {
|
||||
clientSession.getParentSession().setTimestamp(timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -149,7 +163,7 @@ class CodeGenerateUtil {
|
||||
private CodeJWT codeJWT;
|
||||
|
||||
@Override
|
||||
public AuthenticatedClientSessionModel parseSession(String code, KeycloakSession session, RealmModel realm, EventBuilder event) {
|
||||
public AuthenticatedClientSessionModel parseSession(String code, KeycloakSession session, RealmModel realm, ClientModel client, EventBuilder event) {
|
||||
SecretKey aesKey = session.keys().getActiveAesKey(realm).getSecretKey();
|
||||
SecretKey hmacKey = session.keys().getActiveHmacKey(realm).getSecretKey();
|
||||
|
||||
@@ -241,6 +255,16 @@ class CodeGenerateUtil {
|
||||
public boolean isExpired(KeycloakSession session, String code, AuthenticatedClientSessionModel clientSession) {
|
||||
return !codeJWT.isActive();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTimestamp(AuthenticatedClientSessionModel clientSession) {
|
||||
return clientSession.getTimestamp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTimestamp(AuthenticatedClientSessionModel clientSession, int timestamp) {
|
||||
clientSession.setTimestamp(timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -292,7 +292,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
||||
|
||||
// Create AuthenticationSessionModel with same ID like userSession and refresh cookie
|
||||
UserSessionModel userSession = cookieResult.getSession();
|
||||
AuthenticationSessionModel authSession = session.authenticationSessions().createAuthenticationSession(userSession.getId(), realmModel, client);
|
||||
AuthenticationSessionModel authSession = session.authenticationSessions().createRootAuthenticationSession(userSession.getId(), realmModel).createAuthenticationSession(client);
|
||||
new AuthenticationSessionManager(session).setAuthSessionCookie(userSession.getId(), realmModel);
|
||||
|
||||
ClientSessionCode<AuthenticationSessionModel> clientSessionCode = new ClientSessionCode<>(session, realmModel, authSession);
|
||||
@@ -588,7 +588,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
||||
AuthenticationSessionModel authSession = clientSessionCode.getClientSession();
|
||||
|
||||
try {
|
||||
this.event.detail(Details.CODE_ID, authSession.getId())
|
||||
this.event.detail(Details.CODE_ID, authSession.getParentSession().getId())
|
||||
.removeDetail("auth_method");
|
||||
|
||||
SerializedBrokeredIdentityContext serializedCtx = SerializedBrokeredIdentityContext.readFromAuthenticationSession(authSession, AbstractIdpAuthenticator.BROKERED_CONTEXT_NOTE);
|
||||
@@ -685,7 +685,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
||||
|
||||
logger.debugf("Redirect to postBrokerLogin flow after authentication with identityProvider '%s'.", context.getIdpConfig().getAlias());
|
||||
|
||||
authSession.setTimestamp(Time.currentTime());
|
||||
authSession.getParentSession().setTimestamp(Time.currentTime());
|
||||
|
||||
SerializedBrokeredIdentityContext ctx = SerializedBrokeredIdentityContext.serialize(context);
|
||||
ctx.saveToAuthenticationSession(authSession, PostBrokerLoginConstants.PBL_BROKERED_IDENTITY_CONTEXT);
|
||||
@@ -789,7 +789,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
||||
if (nextRequiredAction != null) {
|
||||
return AuthenticationManager.redirectToRequiredActions(session, realmModel, authSession, uriInfo, nextRequiredAction);
|
||||
} else {
|
||||
event.detail(Details.CODE_ID, authSession.getId()); // todo This should be set elsewhere. find out why tests fail. Don't know where this is supposed to be set
|
||||
event.detail(Details.CODE_ID, authSession.getParentSession().getId()); // todo This should be set elsewhere. find out why tests fail. Don't know where this is supposed to be set
|
||||
return AuthenticationManager.finishedRequiredActions(session, authSession, null, clientConnection, request, uriInfo, event);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +72,7 @@ import org.keycloak.services.util.CacheControlUtil;
|
||||
import org.keycloak.services.util.AuthenticationFlowURLHelper;
|
||||
import org.keycloak.services.util.BrowserHistoryHelper;
|
||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||
import org.keycloak.sessions.RootAuthenticationSessionModel;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.GET;
|
||||
@@ -333,7 +334,8 @@ public class LoginActionsService {
|
||||
public Response resetCredentialsGET(@QueryParam("code") String code,
|
||||
@QueryParam("execution") String execution,
|
||||
@QueryParam("client_id") String clientId) {
|
||||
AuthenticationSessionModel authSession = new AuthenticationSessionManager(session).getCurrentAuthenticationSession(realm);
|
||||
ClientModel client = realm.getClientByClientId(clientId);
|
||||
AuthenticationSessionModel authSession = new AuthenticationSessionManager(session).getCurrentAuthenticationSession(realm, client);
|
||||
|
||||
// we allow applications to link to reset credentials without going through OAuth or SAML handshakes
|
||||
if (authSession == null && code == null) {
|
||||
@@ -357,7 +359,10 @@ public class LoginActionsService {
|
||||
|
||||
// set up the account service as the endpoint to call.
|
||||
ClientModel client = realm.getClientByClientId(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID);
|
||||
authSession = new AuthenticationSessionManager(session).createAuthenticationSession(realm, client, true);
|
||||
|
||||
RootAuthenticationSessionModel rootAuthSession = new AuthenticationSessionManager(session).createAuthenticationSession(realm, true);
|
||||
authSession = rootAuthSession.createAuthenticationSession(client);
|
||||
|
||||
authSession.setAction(AuthenticationSessionModel.Action.AUTHENTICATE.name());
|
||||
//authSession.setNote(AuthenticationManager.END_AFTER_REQUIRED_ACTIONS, "true");
|
||||
authSession.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||
@@ -413,9 +418,8 @@ public class LoginActionsService {
|
||||
ActionTokenContext<T> tokenContext;
|
||||
String eventError = null;
|
||||
String defaultErrorMessage = null;
|
||||
AuthenticationSessionModel authSession = new AuthenticationSessionManager(session).getCurrentAuthenticationSession(realm);
|
||||
|
||||
event.event(EventType.EXECUTE_ACTION_TOKEN);
|
||||
AuthenticationSessionModel authSession = null;
|
||||
|
||||
// Setup client, so error page will contain "back to application" link
|
||||
ClientModel client = null;
|
||||
@@ -424,8 +428,11 @@ public class LoginActionsService {
|
||||
}
|
||||
if (client != null) {
|
||||
session.getContext().setClient(client);
|
||||
authSession = new AuthenticationSessionManager(session).getCurrentAuthenticationSession(realm, client);
|
||||
}
|
||||
|
||||
event.event(EventType.EXECUTE_ACTION_TOKEN);
|
||||
|
||||
// First resolve action token handler
|
||||
try {
|
||||
if (tokenString == null) {
|
||||
@@ -500,7 +507,7 @@ public class LoginActionsService {
|
||||
authSession = handler.startFreshAuthenticationSession(token, tokenContext);
|
||||
tokenContext.setAuthenticationSession(authSession, true);
|
||||
} else if (tokenAuthSessionId == null ||
|
||||
! LoginActionsServiceChecks.doesAuthenticationSessionFromCookieMatchOneFromToken(tokenContext, tokenAuthSessionId)) {
|
||||
! LoginActionsServiceChecks.doesAuthenticationSessionFromCookieMatchOneFromToken(tokenContext, tokenAuthSessionId, client)) {
|
||||
// There exists an authentication session but no auth session ID was received in the action token
|
||||
logger.debugf("Authentication session in progress but no authentication session ID was found in action token %s, restarting.", token.getId());
|
||||
new AuthenticationSessionManager(session).removeAuthenticationSession(realm, authSession, false);
|
||||
@@ -737,7 +744,7 @@ public class LoginActionsService {
|
||||
|
||||
public static Response redirectToAfterBrokerLoginEndpoint(KeycloakSession session, RealmModel realm, UriInfo uriInfo, AuthenticationSessionModel authSession, boolean firstBrokerLogin) {
|
||||
ClientSessionCode<AuthenticationSessionModel> accessCode = new ClientSessionCode<>(session, realm, authSession);
|
||||
authSession.setTimestamp(Time.currentTime());
|
||||
authSession.getParentSession().setTimestamp(Time.currentTime());
|
||||
|
||||
String clientId = authSession.getClient().getClientId();
|
||||
URI redirect = firstBrokerLogin ? Urls.identityProviderAfterFirstBrokerLogin(uriInfo.getBaseUri(), realm.getName(), accessCode.getOrGenerateCode(), clientId) :
|
||||
@@ -816,7 +823,7 @@ public class LoginActionsService {
|
||||
OIDCResponseMode responseMode = OIDCResponseMode.parse(respMode, OIDCResponseType.parse(responseType));
|
||||
|
||||
event.event(EventType.LOGIN).client(authSession.getClient())
|
||||
.detail(Details.CODE_ID, authSession.getId())
|
||||
.detail(Details.CODE_ID, authSession.getParentSession().getId())
|
||||
.detail(Details.REDIRECT_URI, authSession.getRedirectUri())
|
||||
.detail(Details.AUTH_METHOD, authSession.getProtocol())
|
||||
.detail(Details.RESPONSE_TYPE, responseType)
|
||||
|
||||
+10
-8
@@ -35,6 +35,8 @@ import org.keycloak.sessions.CommonClientSessionModel.Action;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.sessions.RootAuthenticationSessionModel;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author hmlnarik
|
||||
@@ -75,7 +77,6 @@ public class LoginActionsServiceChecks {
|
||||
* If there is an action required in the session, furthermore it is not the expected one, and the required
|
||||
* action is redirection to "required actions", it throws with response performing the redirect to required
|
||||
* actions.
|
||||
* @param <T>
|
||||
*/
|
||||
public static class IsActionRequired implements Predicate<JsonWebToken> {
|
||||
|
||||
@@ -250,7 +251,7 @@ public class LoginActionsServiceChecks {
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public static <T extends JsonWebToken> boolean doesAuthenticationSessionFromCookieMatchOneFromToken(ActionTokenContext<T> context, String authSessionIdFromToken) throws VerificationException {
|
||||
public static <T extends JsonWebToken> boolean doesAuthenticationSessionFromCookieMatchOneFromToken(ActionTokenContext<T> context, String authSessionIdFromToken, ClientModel client) throws VerificationException {
|
||||
if (authSessionIdFromToken == null) {
|
||||
return false;
|
||||
}
|
||||
@@ -262,9 +263,8 @@ public class LoginActionsServiceChecks {
|
||||
return false;
|
||||
}
|
||||
|
||||
AuthenticationSessionModel authSessionFromCookie = context.getSession()
|
||||
.authenticationSessions().getAuthenticationSession(context.getRealm(), authSessionIdFromCookie);
|
||||
if (authSessionFromCookie == null) { // Cookie contains ID of expired auth session
|
||||
AuthenticationSessionModel authSessionFromCookie = asm.getAuthenticationSessionByIdAndClient(context.getRealm(), authSessionIdFromCookie, client);
|
||||
if (authSessionFromCookie == null) { // Not our client in root session
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -278,14 +278,16 @@ public class LoginActionsServiceChecks {
|
||||
return false;
|
||||
}
|
||||
|
||||
AuthenticationSessionModel authSessionFromParent = context.getSession()
|
||||
.authenticationSessions().getAuthenticationSession(context.getRealm(), parentSessionId);
|
||||
AuthenticationSessionModel authSessionFromParent = asm.getAuthenticationSessionByIdAndClient(context.getRealm(), parentSessionId, client);
|
||||
if (authSessionFromParent == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// It's the correct browser. Let's remove forked session as we won't continue
|
||||
// from the login form (browser flow) but from the token's flow
|
||||
// Don't expire KC_RESTART cookie at this point
|
||||
asm.removeAuthenticationSession(context.getRealm(), authSessionFromCookie, false);
|
||||
LOG.debugf("Removed forked session: %s", authSessionFromCookie.getId());
|
||||
LOG.debugf("Removed forked session: %s", authSessionFromCookie.getParentSession().getId());
|
||||
|
||||
// Refresh browser cookie
|
||||
asm.setAuthSessionCookie(parentSessionId, context.getRealm());
|
||||
|
||||
@@ -47,6 +47,7 @@ import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.services.util.BrowserHistoryHelper;
|
||||
import org.keycloak.services.util.AuthenticationFlowURLHelper;
|
||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||
import org.keycloak.sessions.RootAuthenticationSessionModel;
|
||||
|
||||
|
||||
public class SessionCodeChecks {
|
||||
@@ -132,12 +133,6 @@ public class SessionCodeChecks {
|
||||
return null;
|
||||
}
|
||||
|
||||
// object retrieve
|
||||
AuthenticationSessionModel authSession = ClientSessionCode.getClientSession(code, session, realm, event, AuthenticationSessionModel.class);
|
||||
if (authSession != null) {
|
||||
return authSession;
|
||||
}
|
||||
|
||||
// Setup client to be shown on error/info page based on "client_id" parameter
|
||||
logger.debugf("Will use client '%s' in back-to-application link", clientId);
|
||||
ClientModel client = null;
|
||||
@@ -148,8 +143,16 @@ public class SessionCodeChecks {
|
||||
session.getContext().setClient(client);
|
||||
}
|
||||
|
||||
// object retrieve
|
||||
AuthenticationSessionManager authSessionManager = new AuthenticationSessionManager(session);
|
||||
AuthenticationSessionModel authSession = authSessionManager.getCurrentAuthenticationSession(realm, client);
|
||||
if (authSession != null) {
|
||||
return authSession;
|
||||
}
|
||||
|
||||
// See if we are already authenticated and userSession with same ID exists.
|
||||
String sessionId = new AuthenticationSessionManager(session).getCurrentAuthenticationSessionId(realm);
|
||||
String sessionId = authSessionManager.getCurrentAuthenticationSessionId(realm);
|
||||
RootAuthenticationSessionModel existingRootAuthSession = null;
|
||||
if (sessionId != null) {
|
||||
UserSessionModel userSession = session.sessions().getUserSession(realm, sessionId);
|
||||
if (userSession != null) {
|
||||
@@ -164,10 +167,13 @@ public class SessionCodeChecks {
|
||||
response = loginForm.createInfoPage();
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
existingRootAuthSession = session.authenticationSessions().getRootAuthenticationSession(realm, sessionId);
|
||||
}
|
||||
|
||||
// Otherwise just try to restart from the cookie
|
||||
response = restartAuthenticationSessionFromCookie();
|
||||
response = restartAuthenticationSessionFromCookie(existingRootAuthSession);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -186,7 +192,7 @@ public class SessionCodeChecks {
|
||||
}
|
||||
|
||||
// Client checks
|
||||
event.detail(Details.CODE_ID, authSession.getId());
|
||||
event.detail(Details.CODE_ID, authSession.getParentSession().getId());
|
||||
ClientModel client = authSession.getClient();
|
||||
if (client == null) {
|
||||
event.error(Errors.CLIENT_NOT_FOUND);
|
||||
@@ -240,7 +246,7 @@ public class SessionCodeChecks {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
ClientSessionCode.ParseResult<AuthenticationSessionModel> result = ClientSessionCode.parseResult(code, session, realm, event, AuthenticationSessionModel.class);
|
||||
ClientSessionCode.ParseResult<AuthenticationSessionModel> result = ClientSessionCode.parseResult(code, session, realm, client, event, AuthenticationSessionModel.class);
|
||||
clientCode = result.getCode();
|
||||
if (clientCode == null) {
|
||||
|
||||
@@ -341,11 +347,12 @@ public class SessionCodeChecks {
|
||||
}
|
||||
|
||||
|
||||
private Response restartAuthenticationSessionFromCookie() {
|
||||
private Response restartAuthenticationSessionFromCookie(RootAuthenticationSessionModel existingRootSession) {
|
||||
logger.debug("Authentication session not found. Trying to restart from cookie.");
|
||||
AuthenticationSessionModel authSession = null;
|
||||
|
||||
try {
|
||||
authSession = RestartLoginCookie.restartSession(session, realm);
|
||||
authSession = RestartLoginCookie.restartSession(session, realm, existingRootSession, clientId);
|
||||
} catch (Exception e) {
|
||||
ServicesLogger.LOGGER.failedToParseRestartLoginCookie(e);
|
||||
}
|
||||
|
||||
+3
-1
@@ -52,6 +52,7 @@ import org.keycloak.services.Urls;
|
||||
import org.keycloak.services.managers.AppAuthManager;
|
||||
import org.keycloak.services.managers.Auth;
|
||||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
import org.keycloak.services.managers.AuthenticationSessionManager;
|
||||
import org.keycloak.services.managers.UserSessionManager;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.services.resources.AbstractSecuredLocalService;
|
||||
@@ -60,6 +61,7 @@ import org.keycloak.services.resources.RealmsResource;
|
||||
import org.keycloak.services.util.ResolveRelative;
|
||||
import org.keycloak.services.validation.Validation;
|
||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||
import org.keycloak.sessions.RootAuthenticationSessionModel;
|
||||
import org.keycloak.storage.ReadOnlyException;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
@@ -179,7 +181,7 @@ public class AccountFormService extends AbstractSecuredLocalService {
|
||||
setReferrerOnPage();
|
||||
|
||||
UserSessionModel userSession = auth.getSession();
|
||||
AuthenticationSessionModel authSession = session.authenticationSessions().getAuthenticationSession(realm, userSession.getId());
|
||||
AuthenticationSessionModel authSession = new AuthenticationSessionManager(session).getAuthenticationSessionByIdAndClient(realm, userSession.getId(), client);
|
||||
if (authSession != null) {
|
||||
String forwardedError = authSession.getAuthNote(ACCOUNT_MGMT_FORWARDED_ERROR_NOTE);
|
||||
if (forwardedError != null) {
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.broker.provider.IdentityBrokerException;
|
||||
import org.keycloak.broker.provider.ExchangeTokenToIdentityProviderToken;
|
||||
import org.keycloak.broker.provider.IdentityProvider;
|
||||
import org.keycloak.broker.provider.util.IdentityBrokerState;
|
||||
import org.keycloak.broker.social.SocialIdentityProvider;
|
||||
import org.keycloak.common.ClientConnection;
|
||||
import org.keycloak.events.Details;
|
||||
@@ -197,7 +198,9 @@ public class TwitterIdentityProvider extends AbstractIdentityProvider<OAuth2Iden
|
||||
|
||||
twitter.setOAuthConsumer(getConfig().getClientId(), getConfig().getClientSecret());
|
||||
|
||||
authSession = ClientSessionCode.getClientSession(state, session, realm, event, AuthenticationSessionModel.class);
|
||||
String clientId = IdentityBrokerState.encoded(state).getClientId();
|
||||
ClientModel client = realm.getClientByClientId(clientId);
|
||||
authSession = ClientSessionCode.getClientSession(state, session, realm, client, event, AuthenticationSessionModel.class);
|
||||
|
||||
String twitterToken = authSession.getAuthNote(TWITTER_TOKEN);
|
||||
String twitterSecret = authSession.getAuthNote(TWITTER_TOKENSECRET);
|
||||
|
||||
Reference in new issue
Block a user