KEYCLOAK-4016 Provide a Link to go Back to The Application on a Timeout
This commit is contained in:
parent
ca8577fb4a
commit
8adde64e2c
38 changed files with 541 additions and 191 deletions
|
@ -326,6 +326,10 @@ public class LDAPOperationManager {
|
||||||
filter = "(&(objectClass=*)(" + getUuidAttributeName() + LDAPConstants.EQUAL + id + "))";
|
filter = "(&(objectClass=*)(" + getUuidAttributeName() + LDAPConstants.EQUAL + id + "))";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (logger.isTraceEnabled()) {
|
||||||
|
logger.tracef("Using filter for lookup user by LDAP ID: %s", filter);
|
||||||
|
}
|
||||||
|
|
||||||
return filter;
|
return filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package org.keycloak.broker.provider;
|
package org.keycloak.broker.provider;
|
||||||
|
|
||||||
import org.jboss.resteasy.spi.HttpRequest;
|
import org.jboss.resteasy.spi.HttpRequest;
|
||||||
|
import org.keycloak.broker.provider.util.IdentityBrokerState;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||||
|
@ -30,13 +31,13 @@ public class AuthenticationRequest {
|
||||||
|
|
||||||
private final KeycloakSession session;
|
private final KeycloakSession session;
|
||||||
private final UriInfo uriInfo;
|
private final UriInfo uriInfo;
|
||||||
private final String state;
|
private final IdentityBrokerState state;
|
||||||
private final HttpRequest httpRequest;
|
private final HttpRequest httpRequest;
|
||||||
private final RealmModel realm;
|
private final RealmModel realm;
|
||||||
private final String redirectUri;
|
private final String redirectUri;
|
||||||
private final AuthenticationSessionModel authSession;
|
private final AuthenticationSessionModel authSession;
|
||||||
|
|
||||||
public AuthenticationRequest(KeycloakSession session, RealmModel realm, AuthenticationSessionModel authSession, HttpRequest httpRequest, UriInfo uriInfo, String state, String redirectUri) {
|
public AuthenticationRequest(KeycloakSession session, RealmModel realm, AuthenticationSessionModel authSession, HttpRequest httpRequest, UriInfo uriInfo, IdentityBrokerState state, String redirectUri) {
|
||||||
this.session = session;
|
this.session = session;
|
||||||
this.realm = realm;
|
this.realm = realm;
|
||||||
this.httpRequest = httpRequest;
|
this.httpRequest = httpRequest;
|
||||||
|
@ -54,7 +55,7 @@ public class AuthenticationRequest {
|
||||||
return this.uriInfo;
|
return this.uriInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getState() {
|
public IdentityBrokerState getState() {
|
||||||
return this.state;
|
return this.state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.keycloak.broker.provider.util;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encapsulates parsing logic related to state passed to identity provider in "state" (or RelayState) parameter
|
||||||
|
*
|
||||||
|
* Not Thread-safe
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
|
*/
|
||||||
|
public class IdentityBrokerState {
|
||||||
|
|
||||||
|
private String decodedState;
|
||||||
|
private String clientId;
|
||||||
|
private String encodedState;
|
||||||
|
|
||||||
|
private IdentityBrokerState() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IdentityBrokerState decoded(String decodedState, String clientId) {
|
||||||
|
IdentityBrokerState state = new IdentityBrokerState();
|
||||||
|
state.decodedState = decodedState;
|
||||||
|
state.clientId = clientId;
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IdentityBrokerState encoded(String encodedState) {
|
||||||
|
IdentityBrokerState state = new IdentityBrokerState();
|
||||||
|
state.encodedState = encodedState;
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getDecodedState() {
|
||||||
|
if (decodedState == null) {
|
||||||
|
decode();
|
||||||
|
}
|
||||||
|
return decodedState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getClientId() {
|
||||||
|
if (decodedState == null) {
|
||||||
|
decode();
|
||||||
|
}
|
||||||
|
return clientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEncodedState() {
|
||||||
|
if (encodedState == null) {
|
||||||
|
encode();
|
||||||
|
}
|
||||||
|
return encodedState;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void decode() {
|
||||||
|
String[] decoded = DOT.split(encodedState, 0);
|
||||||
|
decodedState = decoded[0];
|
||||||
|
if (decoded.length > 0) {
|
||||||
|
clientId = decoded[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void encode() {
|
||||||
|
encodedState = decodedState + "." + clientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Pattern DOT = Pattern.compile("\\.");
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -22,7 +22,6 @@ import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.utils.FormMessage;
|
import org.keycloak.models.utils.FormMessage;
|
||||||
import org.keycloak.provider.Provider;
|
import org.keycloak.provider.Provider;
|
||||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
|
||||||
|
|
||||||
import javax.ws.rs.core.MultivaluedMap;
|
import javax.ws.rs.core.MultivaluedMap;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
|
|
|
@ -52,8 +52,12 @@ public interface Constants {
|
||||||
int DEFAULT_OFFLINE_SESSION_IDLE_TIMEOUT = 2592000;
|
int DEFAULT_OFFLINE_SESSION_IDLE_TIMEOUT = 2592000;
|
||||||
|
|
||||||
String VERIFY_EMAIL_KEY = "VERIFY_EMAIL_KEY";
|
String VERIFY_EMAIL_KEY = "VERIFY_EMAIL_KEY";
|
||||||
|
String EXECUTION = "execution";
|
||||||
|
String CLIENT_ID = "client_id";
|
||||||
String KEY = "key";
|
String KEY = "key";
|
||||||
|
|
||||||
|
String SKIP_LINK = "skipLink";
|
||||||
|
|
||||||
// Prefix for user attributes used in various "context"data maps
|
// Prefix for user attributes used in various "context"data maps
|
||||||
String USER_ATTRIBUTES_PREFIX = "user.attributes.";
|
String USER_ATTRIBUTES_PREFIX = "user.attributes.";
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ import org.keycloak.models.AuthenticationFlowModel;
|
||||||
import org.keycloak.models.AuthenticatorConfigModel;
|
import org.keycloak.models.AuthenticatorConfigModel;
|
||||||
import org.keycloak.models.AuthenticatedClientSessionModel;
|
import org.keycloak.models.AuthenticatedClientSessionModel;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
|
import org.keycloak.models.Constants;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
@ -485,15 +486,17 @@ public class AuthenticationProcessor {
|
||||||
return LoginActionsService.loginActionsBaseUrl(getUriInfo())
|
return LoginActionsService.loginActionsBaseUrl(getUriInfo())
|
||||||
.path(AuthenticationProcessor.this.flowPath)
|
.path(AuthenticationProcessor.this.flowPath)
|
||||||
.queryParam(OAuth2Constants.CODE, code)
|
.queryParam(OAuth2Constants.CODE, code)
|
||||||
.queryParam("execution", getExecution().getId())
|
.queryParam(Constants.EXECUTION, getExecution().getId())
|
||||||
|
.queryParam(Constants.CLIENT_ID, getAuthenticationSession().getClient().getClientId())
|
||||||
.build(getRealm().getName());
|
.build(getRealm().getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public URI getActionTokenUrl(String tokenString) {
|
public URI getActionTokenUrl(String tokenString) {
|
||||||
return LoginActionsService.actionTokenProcessor(getUriInfo())
|
return LoginActionsService.actionTokenProcessor(getUriInfo())
|
||||||
.queryParam("key", tokenString)
|
.queryParam(Constants.KEY, tokenString)
|
||||||
.queryParam("execution", getExecution().getId())
|
.queryParam(Constants.EXECUTION, getExecution().getId())
|
||||||
|
.queryParam(Constants.CLIENT_ID, getAuthenticationSession().getClient().getClientId())
|
||||||
.build(getRealm().getName());
|
.build(getRealm().getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -501,7 +504,8 @@ public class AuthenticationProcessor {
|
||||||
public URI getRefreshExecutionUrl() {
|
public URI getRefreshExecutionUrl() {
|
||||||
return LoginActionsService.loginActionsBaseUrl(getUriInfo())
|
return LoginActionsService.loginActionsBaseUrl(getUriInfo())
|
||||||
.path(AuthenticationProcessor.this.flowPath)
|
.path(AuthenticationProcessor.this.flowPath)
|
||||||
.queryParam("execution", getExecution().getId())
|
.queryParam(Constants.EXECUTION, getExecution().getId())
|
||||||
|
.queryParam(Constants.CLIENT_ID, getAuthenticationSession().getClient().getClientId())
|
||||||
.build(getRealm().getName());
|
.build(getRealm().getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,8 @@ import org.keycloak.events.EventBuilder;
|
||||||
import org.keycloak.forms.login.LoginFormsProvider;
|
import org.keycloak.forms.login.LoginFormsProvider;
|
||||||
import org.keycloak.models.AuthenticationExecutionModel;
|
import org.keycloak.models.AuthenticationExecutionModel;
|
||||||
import org.keycloak.models.AuthenticatorConfigModel;
|
import org.keycloak.models.AuthenticatorConfigModel;
|
||||||
|
import org.keycloak.models.ClientModel;
|
||||||
|
import org.keycloak.models.Constants;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
@ -247,9 +249,11 @@ public class FormAuthenticationFlow implements AuthenticationFlow {
|
||||||
}
|
}
|
||||||
|
|
||||||
public URI getActionUrl(String executionId, String code) {
|
public URI getActionUrl(String executionId, String code) {
|
||||||
|
ClientModel client = processor.getAuthenticationSession().getClient();
|
||||||
return LoginActionsService.registrationFormProcessor(processor.getUriInfo())
|
return LoginActionsService.registrationFormProcessor(processor.getUriInfo())
|
||||||
.queryParam(OAuth2Constants.CODE, code)
|
.queryParam(OAuth2Constants.CODE, code)
|
||||||
.queryParam("execution", executionId)
|
.queryParam(Constants.EXECUTION, executionId)
|
||||||
|
.queryParam(Constants.CLIENT_ID, client.getClientId())
|
||||||
.build(processor.getRealm().getName());
|
.build(processor.getRealm().getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,8 @@ import org.keycloak.common.ClientConnection;
|
||||||
import org.keycloak.common.util.Time;
|
import org.keycloak.common.util.Time;
|
||||||
import org.keycloak.events.EventBuilder;
|
import org.keycloak.events.EventBuilder;
|
||||||
import org.keycloak.forms.login.LoginFormsProvider;
|
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.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
@ -132,9 +134,11 @@ public class RequiredActionContextResult implements RequiredActionContext {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public URI getActionUrl(String code) {
|
public URI getActionUrl(String code) {
|
||||||
|
ClientModel client = authenticationSession.getClient();
|
||||||
return LoginActionsService.requiredActionProcessor(getUriInfo())
|
return LoginActionsService.requiredActionProcessor(getUriInfo())
|
||||||
.queryParam(OAuth2Constants.CODE, code)
|
.queryParam(OAuth2Constants.CODE, code)
|
||||||
.queryParam("execution", factory.getId())
|
.queryParam(Constants.EXECUTION, factory.getId())
|
||||||
|
.queryParam(Constants.CLIENT_ID, client.getClientId())
|
||||||
.build(getRealm().getName());
|
.build(getRealm().getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ public class ActionTokenContext<T extends JsonWebToken> {
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface ProcessBrokerFlow {
|
public interface ProcessBrokerFlow {
|
||||||
Response brokerLoginFlow(String code, String execution, String flowPath);
|
Response brokerLoginFlow(String code, String execution, String clientId, String flowPath);
|
||||||
};
|
};
|
||||||
|
|
||||||
private final KeycloakSession session;
|
private final KeycloakSession session;
|
||||||
|
@ -158,6 +158,7 @@ public class ActionTokenContext<T extends JsonWebToken> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Response brokerFlow(String code, String flowPath) {
|
public Response brokerFlow(String code, String flowPath) {
|
||||||
return processBrokerFlow.brokerLoginFlow(code, getExecutionId(), flowPath);
|
ClientModel client = authenticationSession.getClient();
|
||||||
|
return processBrokerFlow.brokerLoginFlow(code, getExecutionId(), client.getClientId(), flowPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import org.keycloak.authentication.actiontoken.*;
|
||||||
import org.keycloak.authentication.authenticators.broker.IdpEmailVerificationAuthenticator;
|
import org.keycloak.authentication.authenticators.broker.IdpEmailVerificationAuthenticator;
|
||||||
import org.keycloak.events.*;
|
import org.keycloak.events.*;
|
||||||
import org.keycloak.forms.login.LoginFormsProvider;
|
import org.keycloak.forms.login.LoginFormsProvider;
|
||||||
|
import org.keycloak.models.Constants;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.services.managers.AuthenticationSessionManager;
|
import org.keycloak.services.managers.AuthenticationSessionManager;
|
||||||
import org.keycloak.services.messages.Messages;
|
import org.keycloak.services.messages.Messages;
|
||||||
|
@ -86,7 +87,7 @@ public class IdpVerifyAccountLinkActionTokenHandler extends AbstractActionTokenH
|
||||||
|
|
||||||
return tokenContext.getSession().getProvider(LoginFormsProvider.class)
|
return tokenContext.getSession().getProvider(LoginFormsProvider.class)
|
||||||
.setSuccess(Messages.IDENTITY_PROVIDER_LINK_SUCCESS, token.getIdentityProviderAlias(), token.getIdentityProviderUsername())
|
.setSuccess(Messages.IDENTITY_PROVIDER_LINK_SUCCESS, token.getIdentityProviderAlias(), token.getIdentityProviderUsername())
|
||||||
.setAttribute("skipLink", true)
|
.setAttribute(Constants.SKIP_LINK, true)
|
||||||
.createInfoPage();
|
.createInfoPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -132,7 +132,10 @@ public class IdpEmailVerificationAuthenticator extends AbstractIdpAuthenticator
|
||||||
brokerContext.getUsername(), brokerContext.getIdpConfig().getAlias()
|
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));
|
||||||
String link = builder.queryParam("execution", context.getExecution().getId()).build(realm.getName()).toString();
|
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);
|
long expirationInMinutes = TimeUnit.SECONDS.toMinutes(validityInSecs);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -64,8 +64,9 @@ public class IdentityProviderAuthenticator implements Authenticator {
|
||||||
for (IdentityProviderModel identityProvider : identityProviders) {
|
for (IdentityProviderModel identityProvider : identityProviders) {
|
||||||
if (identityProvider.isEnabled() && providerId.equals(identityProvider.getAlias())) {
|
if (identityProvider.isEnabled() && providerId.equals(identityProvider.getAlias())) {
|
||||||
String accessCode = new ClientSessionCode<>(context.getSession(), context.getRealm(), context.getAuthenticationSession()).getCode();
|
String accessCode = new ClientSessionCode<>(context.getSession(), context.getRealm(), context.getAuthenticationSession()).getCode();
|
||||||
|
String clientId = context.getAuthenticationSession().getClient().getClientId();
|
||||||
Response response = Response.seeOther(
|
Response response = Response.seeOther(
|
||||||
Urls.identityProviderAuthnRequest(context.getUriInfo().getBaseUri(), providerId, context.getRealm().getName(), accessCode))
|
Urls.identityProviderAuthnRequest(context.getUriInfo().getBaseUri(), providerId, context.getRealm().getName(), accessCode, clientId))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
LOG.debugf("Redirecting to %s", providerId);
|
LOG.debugf("Redirecting to %s", providerId);
|
||||||
|
|
|
@ -36,7 +36,6 @@ import java.util.List;
|
||||||
*/
|
*/
|
||||||
public class RegistrationPage implements FormAuthenticator, FormAuthenticatorFactory {
|
public class RegistrationPage implements FormAuthenticator, FormAuthenticatorFactory {
|
||||||
|
|
||||||
public static final String EXECUTION = "execution";
|
|
||||||
public static final String FIELD_PASSWORD_CONFIRM = "password-confirm";
|
public static final String FIELD_PASSWORD_CONFIRM = "password-confirm";
|
||||||
public static final String FIELD_PASSWORD = "password";
|
public static final String FIELD_PASSWORD = "password";
|
||||||
public static final String FIELD_EMAIL = "email";
|
public static final String FIELD_EMAIL = "email";
|
||||||
|
|
|
@ -155,7 +155,7 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
|
||||||
protected UriBuilder createAuthorizationUrl(AuthenticationRequest request) {
|
protected UriBuilder createAuthorizationUrl(AuthenticationRequest request) {
|
||||||
return UriBuilder.fromUri(getConfig().getAuthorizationUrl())
|
return UriBuilder.fromUri(getConfig().getAuthorizationUrl())
|
||||||
.queryParam(OAUTH2_PARAMETER_SCOPE, getConfig().getDefaultScope())
|
.queryParam(OAUTH2_PARAMETER_SCOPE, getConfig().getDefaultScope())
|
||||||
.queryParam(OAUTH2_PARAMETER_STATE, request.getState())
|
.queryParam(OAUTH2_PARAMETER_STATE, request.getState().getEncodedState())
|
||||||
.queryParam(OAUTH2_PARAMETER_RESPONSE_TYPE, "code")
|
.queryParam(OAUTH2_PARAMETER_RESPONSE_TYPE, "code")
|
||||||
.queryParam(OAUTH2_PARAMETER_CLIENT_ID, getConfig().getClientId())
|
.queryParam(OAUTH2_PARAMETER_CLIENT_ID, getConfig().getClientId())
|
||||||
.queryParam(OAUTH2_PARAMETER_REDIRECT_URI, request.getRedirectUri());
|
.queryParam(OAUTH2_PARAMETER_REDIRECT_URI, request.getRedirectUri());
|
||||||
|
|
|
@ -100,7 +100,7 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
|
||||||
.protocolBinding(protocolBinding)
|
.protocolBinding(protocolBinding)
|
||||||
.nameIdPolicy(SAML2NameIDPolicyBuilder.format(nameIDPolicyFormat));
|
.nameIdPolicy(SAML2NameIDPolicyBuilder.format(nameIDPolicyFormat));
|
||||||
JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder()
|
JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder()
|
||||||
.relayState(request.getState());
|
.relayState(request.getState().getEncodedState());
|
||||||
boolean postBinding = getConfig().isPostBindingAuthnRequest();
|
boolean postBinding = getConfig().isPostBindingAuthnRequest();
|
||||||
|
|
||||||
if (getConfig().isWantAuthnRequestsSigned()) {
|
if (getConfig().isWantAuthnRequestsSigned()) {
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
package org.keycloak.forms.login.freemarker;
|
package org.keycloak.forms.login.freemarker;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
|
|
||||||
import org.keycloak.OAuth2Constants;
|
import org.keycloak.OAuth2Constants;
|
||||||
import org.keycloak.authentication.requiredactions.util.UpdateProfileContext;
|
import org.keycloak.authentication.requiredactions.util.UpdateProfileContext;
|
||||||
import org.keycloak.authentication.requiredactions.util.UserUpdateProfileContext;
|
import org.keycloak.authentication.requiredactions.util.UserUpdateProfileContext;
|
||||||
|
@ -40,7 +39,6 @@ import org.keycloak.models.*;
|
||||||
import org.keycloak.models.utils.FormMessage;
|
import org.keycloak.models.utils.FormMessage;
|
||||||
import org.keycloak.services.Urls;
|
import org.keycloak.services.Urls;
|
||||||
import org.keycloak.services.messages.Messages;
|
import org.keycloak.services.messages.Messages;
|
||||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
|
||||||
import org.keycloak.theme.BrowserSecurityHeaderSetup;
|
import org.keycloak.theme.BrowserSecurityHeaderSetup;
|
||||||
import org.keycloak.theme.FreeMarkerException;
|
import org.keycloak.theme.FreeMarkerException;
|
||||||
import org.keycloak.theme.FreeMarkerUtil;
|
import org.keycloak.theme.FreeMarkerUtil;
|
||||||
|
@ -75,7 +73,6 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
||||||
private List<RoleModel> realmRolesRequested;
|
private List<RoleModel> realmRolesRequested;
|
||||||
private MultivaluedMap<String, RoleModel> resourceRolesRequested;
|
private MultivaluedMap<String, RoleModel> resourceRolesRequested;
|
||||||
private List<ProtocolMapperModel> protocolMappersRequested;
|
private List<ProtocolMapperModel> protocolMappersRequested;
|
||||||
private MultivaluedMap<String, String> queryParams;
|
|
||||||
private Map<String, String> httpResponseHeaders = new HashMap<String, String>();
|
private Map<String, String> httpResponseHeaders = new HashMap<String, String>();
|
||||||
private String accessRequestMessage;
|
private String accessRequestMessage;
|
||||||
private URI actionUri;
|
private URI actionUri;
|
||||||
|
@ -146,8 +143,6 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
||||||
ClientModel client = session.getContext().getClient();
|
ClientModel client = session.getContext().getClient();
|
||||||
UriInfo uriInfo = session.getContext().getUri();
|
UriInfo uriInfo = session.getContext().getUri();
|
||||||
|
|
||||||
MultivaluedMap<String, String> queryParameterMap = queryParams != null ? queryParams : new MultivaluedMapImpl<String, String>();
|
|
||||||
|
|
||||||
String requestURI = uriInfo.getBaseUri().getPath();
|
String requestURI = uriInfo.getBaseUri().getPath();
|
||||||
UriBuilder uriBuilder = UriBuilder.fromUri(requestURI);
|
UriBuilder uriBuilder = UriBuilder.fromUri(requestURI);
|
||||||
if (page == LoginFormsPages.OAUTH_GRANT) {
|
if (page == LoginFormsPages.OAUTH_GRANT) {
|
||||||
|
@ -155,19 +150,17 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
||||||
uriBuilder.replaceQuery(null);
|
uriBuilder.replaceQuery(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (client != null) {
|
||||||
|
uriBuilder.queryParam(Constants.CLIENT_ID, client.getClientId());
|
||||||
|
}
|
||||||
|
|
||||||
URI baseUri = uriBuilder.build();
|
URI baseUri = uriBuilder.build();
|
||||||
|
|
||||||
if (accessCode != null) {
|
if (accessCode != null) {
|
||||||
uriBuilder.queryParam(OAuth2Constants.CODE, accessCode);
|
uriBuilder.queryParam(OAuth2Constants.CODE, accessCode);
|
||||||
}
|
}
|
||||||
URI baseUriWithCode = uriBuilder.build();
|
|
||||||
|
|
||||||
for (String k : queryParameterMap.keySet()) {
|
URI baseUriWithCodeAndClientId = uriBuilder.build();
|
||||||
|
|
||||||
Object[] objects = queryParameterMap.get(k).toArray();
|
|
||||||
if (objects.length == 1 && objects[0] == null) continue; //
|
|
||||||
uriBuilder.replaceQueryParam(k, objects);
|
|
||||||
}
|
|
||||||
|
|
||||||
ThemeProvider themeProvider = session.getProvider(ThemeProvider.class, "extending");
|
ThemeProvider themeProvider = session.getProvider(ThemeProvider.class, "extending");
|
||||||
Theme theme;
|
Theme theme;
|
||||||
|
@ -220,7 +213,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
||||||
|
|
||||||
List<IdentityProviderModel> identityProviders = realm.getIdentityProviders();
|
List<IdentityProviderModel> identityProviders = realm.getIdentityProviders();
|
||||||
identityProviders = LoginFormsUtil.filterIdentityProviders(identityProviders, session, realm, attributes, formData);
|
identityProviders = LoginFormsUtil.filterIdentityProviders(identityProviders, session, realm, attributes, formData);
|
||||||
attributes.put("social", new IdentityProviderBean(realm, session, identityProviders, baseUriWithCode));
|
attributes.put("social", new IdentityProviderBean(realm, session, identityProviders, baseUriWithCodeAndClientId));
|
||||||
|
|
||||||
attributes.put("url", new UrlBean(realm, theme, baseUri, this.actionUri));
|
attributes.put("url", new UrlBean(realm, theme, baseUri, this.actionUri));
|
||||||
|
|
||||||
|
@ -301,16 +294,11 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
||||||
ClientModel client = session.getContext().getClient();
|
ClientModel client = session.getContext().getClient();
|
||||||
UriInfo uriInfo = session.getContext().getUri();
|
UriInfo uriInfo = session.getContext().getUri();
|
||||||
|
|
||||||
MultivaluedMap<String, String> queryParameterMap = queryParams != null ? queryParams : new MultivaluedMapImpl<String, String>();
|
|
||||||
|
|
||||||
String requestURI = uriInfo.getBaseUri().getPath();
|
String requestURI = uriInfo.getBaseUri().getPath();
|
||||||
UriBuilder uriBuilder = UriBuilder.fromUri(requestURI);
|
UriBuilder uriBuilder = UriBuilder.fromUri(requestURI);
|
||||||
|
|
||||||
for (String k : queryParameterMap.keySet()) {
|
if (client != null) {
|
||||||
|
uriBuilder.queryParam(Constants.CLIENT_ID, client.getClientId());
|
||||||
Object[] objects = queryParameterMap.get(k).toArray();
|
|
||||||
if (objects.length == 1 && objects[0] == null) continue; //
|
|
||||||
uriBuilder.replaceQueryParam(k, objects);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
URI baseUri = uriBuilder.build();
|
URI baseUri = uriBuilder.build();
|
||||||
|
@ -318,6 +306,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
||||||
if (accessCode != null) {
|
if (accessCode != null) {
|
||||||
uriBuilder.queryParam(OAuth2Constants.CODE, accessCode);
|
uriBuilder.queryParam(OAuth2Constants.CODE, accessCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
URI baseUriWithCode = uriBuilder.build();
|
URI baseUriWithCode = uriBuilder.build();
|
||||||
|
|
||||||
ThemeProvider themeProvider = session.getProvider(ThemeProvider.class, "extending");
|
ThemeProvider themeProvider = session.getProvider(ThemeProvider.class, "extending");
|
||||||
|
|
|
@ -18,6 +18,7 @@ package org.keycloak.services;
|
||||||
|
|
||||||
import org.keycloak.OAuth2Constants;
|
import org.keycloak.OAuth2Constants;
|
||||||
import org.keycloak.common.Version;
|
import org.keycloak.common.Version;
|
||||||
|
import org.keycloak.models.Constants;
|
||||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||||
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
|
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
|
||||||
import org.keycloak.services.resources.AccountService;
|
import org.keycloak.services.resources.AccountService;
|
||||||
|
@ -73,13 +74,16 @@ public class Urls {
|
||||||
.build(realmName, providerId);
|
.build(realmName, providerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static URI identityProviderAuthnRequest(URI baseUri, String providerId, String realmName, String accessCode) {
|
public static URI identityProviderAuthnRequest(URI baseUri, String providerId, String realmName, String accessCode, String clientId) {
|
||||||
UriBuilder uriBuilder = realmBase(baseUri).path(RealmsResource.class, "getBrokerService")
|
UriBuilder uriBuilder = realmBase(baseUri).path(RealmsResource.class, "getBrokerService")
|
||||||
.path(IdentityBrokerService.class, "performLogin");
|
.path(IdentityBrokerService.class, "performLogin");
|
||||||
|
|
||||||
if (accessCode != null) {
|
if (accessCode != null) {
|
||||||
uriBuilder.replaceQueryParam(OAuth2Constants.CODE, accessCode);
|
uriBuilder.replaceQueryParam(OAuth2Constants.CODE, accessCode);
|
||||||
}
|
}
|
||||||
|
if (clientId != null) {
|
||||||
|
uriBuilder.replaceQueryParam(Constants.CLIENT_ID, clientId);
|
||||||
|
}
|
||||||
|
|
||||||
return uriBuilder.build(realmName, providerId);
|
return uriBuilder.build(realmName, providerId);
|
||||||
}
|
}
|
||||||
|
@ -99,20 +103,22 @@ public class Urls {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static URI identityProviderAuthnRequest(URI baseURI, String providerId, String realmName) {
|
public static URI identityProviderAuthnRequest(URI baseURI, String providerId, String realmName) {
|
||||||
return identityProviderAuthnRequest(baseURI, providerId, realmName, null);
|
return identityProviderAuthnRequest(baseURI, providerId, realmName, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static URI identityProviderAfterFirstBrokerLogin(URI baseUri, String realmName, String accessCode) {
|
public static URI identityProviderAfterFirstBrokerLogin(URI baseUri, String realmName, String accessCode, String clientId) {
|
||||||
return realmBase(baseUri).path(RealmsResource.class, "getBrokerService")
|
return realmBase(baseUri).path(RealmsResource.class, "getBrokerService")
|
||||||
.path(IdentityBrokerService.class, "afterFirstBrokerLogin")
|
.path(IdentityBrokerService.class, "afterFirstBrokerLogin")
|
||||||
.replaceQueryParam(OAuth2Constants.CODE, accessCode)
|
.replaceQueryParam(OAuth2Constants.CODE, accessCode)
|
||||||
|
.replaceQueryParam(Constants.CLIENT_ID, clientId)
|
||||||
.build(realmName);
|
.build(realmName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static URI identityProviderAfterPostBrokerLogin(URI baseUri, String realmName, String accessCode) {
|
public static URI identityProviderAfterPostBrokerLogin(URI baseUri, String realmName, String accessCode, String clientId) {
|
||||||
return realmBase(baseUri).path(RealmsResource.class, "getBrokerService")
|
return realmBase(baseUri).path(RealmsResource.class, "getBrokerService")
|
||||||
.path(IdentityBrokerService.class, "afterPostBrokerLoginFlow")
|
.path(IdentityBrokerService.class, "afterPostBrokerLoginFlow")
|
||||||
.replaceQueryParam(OAuth2Constants.CODE, accessCode)
|
.replaceQueryParam(OAuth2Constants.CODE, accessCode)
|
||||||
|
.replaceQueryParam(Constants.CLIENT_ID, clientId)
|
||||||
.build(realmName);
|
.build(realmName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -78,9 +78,6 @@ public class AuthenticationManager {
|
||||||
public static final String END_AFTER_REQUIRED_ACTIONS = "END_AFTER_REQUIRED_ACTIONS";
|
public static final String END_AFTER_REQUIRED_ACTIONS = "END_AFTER_REQUIRED_ACTIONS";
|
||||||
public static final String INVALIDATE_ACTION_TOKEN = "INVALIDATE_ACTION_TOKEN";
|
public static final String INVALIDATE_ACTION_TOKEN = "INVALIDATE_ACTION_TOKEN";
|
||||||
|
|
||||||
// Last authenticated client in userSession.
|
|
||||||
public static final String LAST_AUTHENTICATED_CLIENT = "LAST_AUTHENTICATED_CLIENT";
|
|
||||||
|
|
||||||
// userSession note with authTime (time when authentication flow including requiredActions was finished)
|
// userSession note with authTime (time when authentication flow including requiredActions was finished)
|
||||||
public static final String AUTH_TIME = "AUTH_TIME";
|
public static final String AUTH_TIME = "AUTH_TIME";
|
||||||
// clientSession note with flag that clientSession was authenticated through SSO cookie
|
// clientSession note with flag that clientSession was authenticated through SSO cookie
|
||||||
|
@ -95,7 +92,6 @@ public class AuthenticationManager {
|
||||||
public static final String KEYCLOAK_SESSION_COOKIE = "KEYCLOAK_SESSION";
|
public static final String KEYCLOAK_SESSION_COOKIE = "KEYCLOAK_SESSION";
|
||||||
public static final String KEYCLOAK_REMEMBER_ME = "KEYCLOAK_REMEMBER_ME";
|
public static final String KEYCLOAK_REMEMBER_ME = "KEYCLOAK_REMEMBER_ME";
|
||||||
public static final String KEYCLOAK_LOGOUT_PROTOCOL = "KEYCLOAK_LOGOUT_PROTOCOL";
|
public static final String KEYCLOAK_LOGOUT_PROTOCOL = "KEYCLOAK_LOGOUT_PROTOCOL";
|
||||||
public static final String CURRENT_REQUIRED_ACTION = "CURRENT_REQUIRED_ACTION";
|
|
||||||
|
|
||||||
public static boolean isSessionValid(RealmModel realm, UserSessionModel userSession) {
|
public static boolean isSessionValid(RealmModel realm, UserSessionModel userSession) {
|
||||||
if (userSession == null) {
|
if (userSession == null) {
|
||||||
|
@ -463,8 +459,6 @@ public class AuthenticationManager {
|
||||||
userSession.setNote(AUTH_TIME, String.valueOf(authTime));
|
userSession.setNote(AUTH_TIME, String.valueOf(authTime));
|
||||||
}
|
}
|
||||||
|
|
||||||
userSession.setNote(LAST_AUTHENTICATED_CLIENT, clientSession.getClient().getId());
|
|
||||||
|
|
||||||
return protocol.authenticated(userSession, clientSession);
|
return protocol.authenticated(userSession, clientSession);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -496,9 +490,11 @@ public class AuthenticationManager {
|
||||||
.path(LoginActionsService.REQUIRED_ACTION);
|
.path(LoginActionsService.REQUIRED_ACTION);
|
||||||
|
|
||||||
if (requiredAction != null) {
|
if (requiredAction != null) {
|
||||||
uriBuilder.queryParam("execution", requiredAction);
|
uriBuilder.queryParam(Constants.EXECUTION, requiredAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uriBuilder.queryParam(Constants.CLIENT_ID, authSession.getClient().getClientId());
|
||||||
|
|
||||||
URI redirect = uriBuilder.build(realm.getName());
|
URI redirect = uriBuilder.build(realm.getName());
|
||||||
return Response.status(302).location(redirect).build();
|
return Response.status(302).location(redirect).build();
|
||||||
|
|
||||||
|
@ -526,7 +522,7 @@ public class AuthenticationManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
infoPage.setAttribute("skipLink", true);
|
infoPage.setAttribute(Constants.SKIP_LINK, true);
|
||||||
}
|
}
|
||||||
Response response = infoPage
|
Response response = infoPage
|
||||||
.createInfoPage();
|
.createInfoPage();
|
||||||
|
|
|
@ -30,6 +30,7 @@ import org.keycloak.broker.provider.IdentityBrokerException;
|
||||||
import org.keycloak.broker.provider.IdentityProvider;
|
import org.keycloak.broker.provider.IdentityProvider;
|
||||||
import org.keycloak.broker.provider.IdentityProviderFactory;
|
import org.keycloak.broker.provider.IdentityProviderFactory;
|
||||||
import org.keycloak.broker.provider.IdentityProviderMapper;
|
import org.keycloak.broker.provider.IdentityProviderMapper;
|
||||||
|
import org.keycloak.broker.provider.util.IdentityBrokerState;
|
||||||
import org.keycloak.broker.saml.SAMLEndpoint;
|
import org.keycloak.broker.saml.SAMLEndpoint;
|
||||||
import org.keycloak.broker.social.SocialIdentityProvider;
|
import org.keycloak.broker.social.SocialIdentityProvider;
|
||||||
import org.keycloak.common.ClientConnection;
|
import org.keycloak.common.ClientConnection;
|
||||||
|
@ -338,14 +339,14 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
@Path("/{provider_id}/login")
|
@Path("/{provider_id}/login")
|
||||||
public Response performPostLogin(@PathParam("provider_id") String providerId, @QueryParam("code") String code) {
|
public Response performPostLogin(@PathParam("provider_id") String providerId, @QueryParam("code") String code, @QueryParam("client_id") String clientId) {
|
||||||
return performLogin(providerId, code);
|
return performLogin(providerId, code, clientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@NoCache
|
@NoCache
|
||||||
@Path("/{provider_id}/login")
|
@Path("/{provider_id}/login")
|
||||||
public Response performLogin(@PathParam("provider_id") String providerId, @QueryParam("code") String code) {
|
public Response performLogin(@PathParam("provider_id") String providerId, @QueryParam("code") String code, @QueryParam("client_id") String clientId) {
|
||||||
this.event.detail(Details.IDENTITY_PROVIDER, providerId);
|
this.event.detail(Details.IDENTITY_PROVIDER, providerId);
|
||||||
|
|
||||||
if (isDebugEnabled()) {
|
if (isDebugEnabled()) {
|
||||||
|
@ -353,7 +354,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ParsedCodeContext parsedCode = parseClientSessionCode(code);
|
ParsedCodeContext parsedCode = parseSessionCode(code, clientId);
|
||||||
if (parsedCode.response != null) {
|
if (parsedCode.response != null) {
|
||||||
return parsedCode.response;
|
return parsedCode.response;
|
||||||
}
|
}
|
||||||
|
@ -479,7 +480,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
||||||
if (context.getContextData().get(SAMLEndpoint.SAML_IDP_INITIATED_CLIENT_ID) != null) {
|
if (context.getContextData().get(SAMLEndpoint.SAML_IDP_INITIATED_CLIENT_ID) != null) {
|
||||||
parsedCode = samlIdpInitiatedSSO((String) context.getContextData().get(SAMLEndpoint.SAML_IDP_INITIATED_CLIENT_ID));
|
parsedCode = samlIdpInitiatedSSO((String) context.getContextData().get(SAMLEndpoint.SAML_IDP_INITIATED_CLIENT_ID));
|
||||||
} else {
|
} else {
|
||||||
parsedCode = parseClientSessionCode(context.getCode());
|
parsedCode = parseEncodedSessionCode(context.getCode());
|
||||||
}
|
}
|
||||||
if (parsedCode.response != null) {
|
if (parsedCode.response != null) {
|
||||||
return parsedCode.response;
|
return parsedCode.response;
|
||||||
|
@ -549,6 +550,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
||||||
ctx.saveToAuthenticationSession(authenticationSession, AbstractIdpAuthenticator.BROKERED_CONTEXT_NOTE);
|
ctx.saveToAuthenticationSession(authenticationSession, AbstractIdpAuthenticator.BROKERED_CONTEXT_NOTE);
|
||||||
|
|
||||||
URI redirect = LoginActionsService.firstBrokerLoginProcessor(uriInfo)
|
URI redirect = LoginActionsService.firstBrokerLoginProcessor(uriInfo)
|
||||||
|
.queryParam(Constants.CLIENT_ID, authenticationSession.getClient().getClientId())
|
||||||
.build(realmModel.getName());
|
.build(realmModel.getName());
|
||||||
return Response.status(302).location(redirect).build();
|
return Response.status(302).location(redirect).build();
|
||||||
|
|
||||||
|
@ -584,8 +586,8 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
||||||
@GET
|
@GET
|
||||||
@NoCache
|
@NoCache
|
||||||
@Path("/after-first-broker-login")
|
@Path("/after-first-broker-login")
|
||||||
public Response afterFirstBrokerLogin(@QueryParam("code") String code) {
|
public Response afterFirstBrokerLogin(@QueryParam("code") String code, @QueryParam("client_id") String clientId) {
|
||||||
ParsedCodeContext parsedCode = parseClientSessionCode(code);
|
ParsedCodeContext parsedCode = parseSessionCode(code, clientId);
|
||||||
if (parsedCode.response != null) {
|
if (parsedCode.response != null) {
|
||||||
return parsedCode.response;
|
return parsedCode.response;
|
||||||
}
|
}
|
||||||
|
@ -701,6 +703,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
||||||
authSession.setAuthNote(PostBrokerLoginConstants.PBL_AFTER_FIRST_BROKER_LOGIN, String.valueOf(wasFirstBrokerLogin));
|
authSession.setAuthNote(PostBrokerLoginConstants.PBL_AFTER_FIRST_BROKER_LOGIN, String.valueOf(wasFirstBrokerLogin));
|
||||||
|
|
||||||
URI redirect = LoginActionsService.postBrokerLoginProcessor(uriInfo)
|
URI redirect = LoginActionsService.postBrokerLoginProcessor(uriInfo)
|
||||||
|
.queryParam(Constants.CLIENT_ID, authSession.getClient().getClientId())
|
||||||
.build(realmModel.getName());
|
.build(realmModel.getName());
|
||||||
return Response.status(302).location(redirect).build();
|
return Response.status(302).location(redirect).build();
|
||||||
}
|
}
|
||||||
|
@ -711,8 +714,8 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
||||||
@GET
|
@GET
|
||||||
@NoCache
|
@NoCache
|
||||||
@Path("/after-post-broker-login")
|
@Path("/after-post-broker-login")
|
||||||
public Response afterPostBrokerLoginFlow(@QueryParam("code") String code) {
|
public Response afterPostBrokerLoginFlow(@QueryParam("code") String code, @QueryParam("client_id") String clientId) {
|
||||||
ParsedCodeContext parsedCode = parseClientSessionCode(code);
|
ParsedCodeContext parsedCode = parseSessionCode(code, clientId);
|
||||||
if (parsedCode.response != null) {
|
if (parsedCode.response != null) {
|
||||||
return parsedCode.response;
|
return parsedCode.response;
|
||||||
}
|
}
|
||||||
|
@ -804,7 +807,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response cancelled(String code) {
|
public Response cancelled(String code) {
|
||||||
ParsedCodeContext parsedCode = parseClientSessionCode(code);
|
ParsedCodeContext parsedCode = parseEncodedSessionCode(code);
|
||||||
if (parsedCode.response != null) {
|
if (parsedCode.response != null) {
|
||||||
return parsedCode.response;
|
return parsedCode.response;
|
||||||
}
|
}
|
||||||
|
@ -820,7 +823,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response error(String code, String message) {
|
public Response error(String code, String message) {
|
||||||
ParsedCodeContext parsedCode = parseClientSessionCode(code);
|
ParsedCodeContext parsedCode = parseEncodedSessionCode(code);
|
||||||
if (parsedCode.response != null) {
|
if (parsedCode.response != null) {
|
||||||
return parsedCode.response;
|
return parsedCode.response;
|
||||||
}
|
}
|
||||||
|
@ -960,14 +963,21 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ParsedCodeContext parseClientSessionCode(String code) {
|
private ParsedCodeContext parseEncodedSessionCode(String encodedCode) {
|
||||||
if (code == null) {
|
IdentityBrokerState state = IdentityBrokerState.encoded(encodedCode);
|
||||||
logger.debugf("Invalid request. Authorization code was null");
|
String code = state.getDecodedState();
|
||||||
|
String clientId = state.getClientId();
|
||||||
|
return parseSessionCode(code, clientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ParsedCodeContext parseSessionCode(String code, String clientId) {
|
||||||
|
if (code == null || clientId == null) {
|
||||||
|
logger.debugf("Invalid request. Authorization code or clientId was null. Code=" + code + ", clientId=" + clientId);
|
||||||
Response staleCodeError = redirectToErrorPage(Messages.INVALID_REQUEST);
|
Response staleCodeError = redirectToErrorPage(Messages.INVALID_REQUEST);
|
||||||
return ParsedCodeContext.response(staleCodeError);
|
return ParsedCodeContext.response(staleCodeError);
|
||||||
}
|
}
|
||||||
|
|
||||||
SessionCodeChecks checks = new SessionCodeChecks(realmModel, uriInfo, clientConnection, session, event, code, null, LoginActionsService.AUTHENTICATE_PATH);
|
SessionCodeChecks checks = new SessionCodeChecks(realmModel, uriInfo, clientConnection, session, event, code, null, clientId, LoginActionsService.AUTHENTICATE_PATH);
|
||||||
checks.initialVerify();
|
checks.initialVerify();
|
||||||
if (!checks.verifyActiveAndValidAction(AuthenticationSessionModel.Action.AUTHENTICATE.name(), ClientSessionCode.ActionType.LOGIN)) {
|
if (!checks.verifyActiveAndValidAction(AuthenticationSessionModel.Action.AUTHENTICATE.name(), ClientSessionCode.ActionType.LOGIN)) {
|
||||||
|
|
||||||
|
@ -1041,14 +1051,15 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
||||||
|
|
||||||
private AuthenticationRequest createAuthenticationRequest(String providerId, ClientSessionCode<AuthenticationSessionModel> clientSessionCode) {
|
private AuthenticationRequest createAuthenticationRequest(String providerId, ClientSessionCode<AuthenticationSessionModel> clientSessionCode) {
|
||||||
AuthenticationSessionModel authSession = null;
|
AuthenticationSessionModel authSession = null;
|
||||||
String relayState = null;
|
IdentityBrokerState encodedState = null;
|
||||||
|
|
||||||
if (clientSessionCode != null) {
|
if (clientSessionCode != null) {
|
||||||
authSession = clientSessionCode.getClientSession();
|
authSession = clientSessionCode.getClientSession();
|
||||||
relayState = clientSessionCode.getCode();
|
String relayState = clientSessionCode.getCode();
|
||||||
|
encodedState = IdentityBrokerState.decoded(relayState, authSession.getClient().getClientId());
|
||||||
}
|
}
|
||||||
|
|
||||||
return new AuthenticationRequest(this.session, this.realmModel, authSession, this.request, this.uriInfo, relayState, getRedirectUri(providerId));
|
return new AuthenticationRequest(this.session, this.realmModel, authSession, this.request, this.uriInfo, encodedState, getRedirectUri(providerId));
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getRedirectUri(String providerId) {
|
private String getRedirectUri(String providerId) {
|
||||||
|
|
|
@ -179,16 +179,16 @@ public class LoginActionsService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private SessionCodeChecks checksForCode(String code, String execution, String flowPath) {
|
private SessionCodeChecks checksForCode(String code, String execution, String clientId, String flowPath) {
|
||||||
SessionCodeChecks res = new SessionCodeChecks(realm, uriInfo, clientConnection, session, event, code, execution, flowPath);
|
SessionCodeChecks res = new SessionCodeChecks(realm, uriInfo, clientConnection, session, event, code, execution, clientId, flowPath);
|
||||||
res.initialVerify();
|
res.initialVerify();
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected URI getLastExecutionUrl(String flowPath, String executionId) {
|
protected URI getLastExecutionUrl(String flowPath, String executionId, String clientId) {
|
||||||
return new AuthenticationFlowURLHelper(session, realm, uriInfo)
|
return new AuthenticationFlowURLHelper(session, realm, uriInfo)
|
||||||
.getLastExecutionUrl(flowPath, executionId);
|
.getLastExecutionUrl(flowPath, executionId, clientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -199,9 +199,9 @@ public class LoginActionsService {
|
||||||
*/
|
*/
|
||||||
@Path(RESTART_PATH)
|
@Path(RESTART_PATH)
|
||||||
@GET
|
@GET
|
||||||
public Response restartSession() {
|
public Response restartSession(@QueryParam("client_id") String clientId) {
|
||||||
event.event(EventType.RESTART_AUTHENTICATION);
|
event.event(EventType.RESTART_AUTHENTICATION);
|
||||||
SessionCodeChecks checks = new SessionCodeChecks(realm, uriInfo, clientConnection, session, event, null, null, null);
|
SessionCodeChecks checks = new SessionCodeChecks(realm, uriInfo, clientConnection, session, event, null, null, clientId, null);
|
||||||
|
|
||||||
AuthenticationSessionModel authSession = checks.initialVerifyAuthSession();
|
AuthenticationSessionModel authSession = checks.initialVerifyAuthSession();
|
||||||
if (authSession == null) {
|
if (authSession == null) {
|
||||||
|
@ -215,7 +215,7 @@ public class LoginActionsService {
|
||||||
|
|
||||||
AuthenticationProcessor.resetFlow(authSession, flowPath);
|
AuthenticationProcessor.resetFlow(authSession, flowPath);
|
||||||
|
|
||||||
URI redirectUri = getLastExecutionUrl(flowPath, null);
|
URI redirectUri = getLastExecutionUrl(flowPath, null, authSession.getClient().getClientId());
|
||||||
logger.debugf("Flow restart requested. Redirecting to %s", redirectUri);
|
logger.debugf("Flow restart requested. Redirecting to %s", redirectUri);
|
||||||
return Response.status(Response.Status.FOUND).location(redirectUri).build();
|
return Response.status(Response.Status.FOUND).location(redirectUri).build();
|
||||||
}
|
}
|
||||||
|
@ -230,10 +230,11 @@ public class LoginActionsService {
|
||||||
@Path(AUTHENTICATE_PATH)
|
@Path(AUTHENTICATE_PATH)
|
||||||
@GET
|
@GET
|
||||||
public Response authenticate(@QueryParam("code") String code,
|
public Response authenticate(@QueryParam("code") String code,
|
||||||
@QueryParam("execution") String execution) {
|
@QueryParam("execution") String execution,
|
||||||
|
@QueryParam("client_id") String clientId) {
|
||||||
event.event(EventType.LOGIN);
|
event.event(EventType.LOGIN);
|
||||||
|
|
||||||
SessionCodeChecks checks = checksForCode(code, execution, AUTHENTICATE_PATH);
|
SessionCodeChecks checks = checksForCode(code, execution, clientId, AUTHENTICATE_PATH);
|
||||||
if (!checks.verifyActiveAndValidAction(ClientSessionModel.Action.AUTHENTICATE.name(), ClientSessionCode.ActionType.LOGIN)) {
|
if (!checks.verifyActiveAndValidAction(ClientSessionModel.Action.AUTHENTICATE.name(), ClientSessionCode.ActionType.LOGIN)) {
|
||||||
return checks.getResponse();
|
return checks.getResponse();
|
||||||
}
|
}
|
||||||
|
@ -298,22 +299,24 @@ public class LoginActionsService {
|
||||||
@Path(AUTHENTICATE_PATH)
|
@Path(AUTHENTICATE_PATH)
|
||||||
@POST
|
@POST
|
||||||
public Response authenticateForm(@QueryParam("code") String code,
|
public Response authenticateForm(@QueryParam("code") String code,
|
||||||
@QueryParam("execution") String execution) {
|
@QueryParam("execution") String execution,
|
||||||
return authenticate(code, execution);
|
@QueryParam("client_id") String clientId) {
|
||||||
|
return authenticate(code, execution, clientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Path(RESET_CREDENTIALS_PATH)
|
@Path(RESET_CREDENTIALS_PATH)
|
||||||
@POST
|
@POST
|
||||||
public Response resetCredentialsPOST(@QueryParam("code") String code,
|
public Response resetCredentialsPOST(@QueryParam("code") String code,
|
||||||
@QueryParam("execution") String execution,
|
@QueryParam("execution") String execution,
|
||||||
|
@QueryParam("client_id") String clientId,
|
||||||
@QueryParam(Constants.KEY) String key) {
|
@QueryParam(Constants.KEY) String key) {
|
||||||
if (key != null) {
|
if (key != null) {
|
||||||
return handleActionToken(key, execution);
|
return handleActionToken(key, execution, clientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
event.event(EventType.RESET_PASSWORD);
|
event.event(EventType.RESET_PASSWORD);
|
||||||
|
|
||||||
return resetCredentials(code, execution);
|
return resetCredentials(code, execution, clientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -327,7 +330,8 @@ public class LoginActionsService {
|
||||||
@Path(RESET_CREDENTIALS_PATH)
|
@Path(RESET_CREDENTIALS_PATH)
|
||||||
@GET
|
@GET
|
||||||
public Response resetCredentialsGET(@QueryParam("code") String code,
|
public Response resetCredentialsGET(@QueryParam("code") String code,
|
||||||
@QueryParam("execution") String execution) {
|
@QueryParam("execution") String execution,
|
||||||
|
@QueryParam("client_id") String clientId) {
|
||||||
AuthenticationSessionModel authSession = new AuthenticationSessionManager(session).getCurrentAuthenticationSession(realm);
|
AuthenticationSessionModel authSession = new AuthenticationSessionManager(session).getCurrentAuthenticationSession(realm);
|
||||||
|
|
||||||
// we allow applications to link to reset credentials without going through OAuth or SAML handshakes
|
// we allow applications to link to reset credentials without going through OAuth or SAML handshakes
|
||||||
|
@ -343,7 +347,7 @@ public class LoginActionsService {
|
||||||
}
|
}
|
||||||
|
|
||||||
event.event(EventType.RESET_PASSWORD);
|
event.event(EventType.RESET_PASSWORD);
|
||||||
return resetCredentials(code, execution);
|
return resetCredentials(code, execution, clientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
AuthenticationSessionModel createAuthenticationSessionForClient()
|
AuthenticationSessionModel createAuthenticationSessionForClient()
|
||||||
|
@ -370,8 +374,8 @@ public class LoginActionsService {
|
||||||
* @param execution
|
* @param execution
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
protected Response resetCredentials(String code, String execution) {
|
protected Response resetCredentials(String code, String execution, String clientId) {
|
||||||
SessionCodeChecks checks = checksForCode(code, execution, RESET_CREDENTIALS_PATH);
|
SessionCodeChecks checks = checksForCode(code, execution, clientId, RESET_CREDENTIALS_PATH);
|
||||||
if (!checks.verifyActiveAndValidAction(ClientSessionModel.Action.AUTHENTICATE.name(), ClientSessionCode.ActionType.USER)) {
|
if (!checks.verifyActiveAndValidAction(ClientSessionModel.Action.AUTHENTICATE.name(), ClientSessionCode.ActionType.USER)) {
|
||||||
return checks.getResponse();
|
return checks.getResponse();
|
||||||
}
|
}
|
||||||
|
@ -397,11 +401,12 @@ public class LoginActionsService {
|
||||||
@Path("action-token")
|
@Path("action-token")
|
||||||
@GET
|
@GET
|
||||||
public Response executeActionToken(@QueryParam("key") String key,
|
public Response executeActionToken(@QueryParam("key") String key,
|
||||||
@QueryParam("execution") String execution) {
|
@QueryParam("execution") String execution,
|
||||||
return handleActionToken(key, execution);
|
@QueryParam("client_id") String clientId) {
|
||||||
|
return handleActionToken(key, execution, clientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <T extends DefaultActionToken> Response handleActionToken(String tokenString, String execution) {
|
protected <T extends DefaultActionToken> Response handleActionToken(String tokenString, String execution, String clientId) {
|
||||||
T token;
|
T token;
|
||||||
ActionTokenHandler<T> handler;
|
ActionTokenHandler<T> handler;
|
||||||
ActionTokenContext<T> tokenContext;
|
ActionTokenContext<T> tokenContext;
|
||||||
|
@ -411,6 +416,15 @@ public class LoginActionsService {
|
||||||
|
|
||||||
event.event(EventType.EXECUTE_ACTION_TOKEN);
|
event.event(EventType.EXECUTE_ACTION_TOKEN);
|
||||||
|
|
||||||
|
// Setup client, so error page will contain "back to application" link
|
||||||
|
ClientModel client = null;
|
||||||
|
if (clientId != null) {
|
||||||
|
client = realm.getClientByClientId(clientId);
|
||||||
|
}
|
||||||
|
if (client != null) {
|
||||||
|
session.getContext().setClient(client);
|
||||||
|
}
|
||||||
|
|
||||||
// First resolve action token handler
|
// First resolve action token handler
|
||||||
try {
|
try {
|
||||||
if (tokenString == null) {
|
if (tokenString == null) {
|
||||||
|
@ -570,8 +584,9 @@ public class LoginActionsService {
|
||||||
@Path(REGISTRATION_PATH)
|
@Path(REGISTRATION_PATH)
|
||||||
@GET
|
@GET
|
||||||
public Response registerPage(@QueryParam("code") String code,
|
public Response registerPage(@QueryParam("code") String code,
|
||||||
@QueryParam("execution") String execution) {
|
@QueryParam("execution") String execution,
|
||||||
return registerRequest(code, execution, false);
|
@QueryParam("client_id") String clientId) {
|
||||||
|
return registerRequest(code, execution, clientId, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -584,19 +599,20 @@ public class LoginActionsService {
|
||||||
@Path(REGISTRATION_PATH)
|
@Path(REGISTRATION_PATH)
|
||||||
@POST
|
@POST
|
||||||
public Response processRegister(@QueryParam("code") String code,
|
public Response processRegister(@QueryParam("code") String code,
|
||||||
@QueryParam("execution") String execution) {
|
@QueryParam("execution") String execution,
|
||||||
return registerRequest(code, execution, true);
|
@QueryParam("client_id") String clientId) {
|
||||||
|
return registerRequest(code, execution, clientId, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private Response registerRequest(String code, String execution, boolean isPostRequest) {
|
private Response registerRequest(String code, String execution, String clientId, boolean isPostRequest) {
|
||||||
event.event(EventType.REGISTER);
|
event.event(EventType.REGISTER);
|
||||||
if (!realm.isRegistrationAllowed()) {
|
if (!realm.isRegistrationAllowed()) {
|
||||||
event.error(Errors.REGISTRATION_DISABLED);
|
event.error(Errors.REGISTRATION_DISABLED);
|
||||||
return ErrorPage.error(session, Messages.REGISTRATION_NOT_ALLOWED);
|
return ErrorPage.error(session, Messages.REGISTRATION_NOT_ALLOWED);
|
||||||
}
|
}
|
||||||
|
|
||||||
SessionCodeChecks checks = checksForCode(code, execution, REGISTRATION_PATH);
|
SessionCodeChecks checks = checksForCode(code, execution, clientId, REGISTRATION_PATH);
|
||||||
if (!checks.verifyActiveAndValidAction(ClientSessionModel.Action.AUTHENTICATE.name(), ClientSessionCode.ActionType.LOGIN)) {
|
if (!checks.verifyActiveAndValidAction(ClientSessionModel.Action.AUTHENTICATE.name(), ClientSessionCode.ActionType.LOGIN)) {
|
||||||
return checks.getResponse();
|
return checks.getResponse();
|
||||||
}
|
}
|
||||||
|
@ -612,39 +628,43 @@ public class LoginActionsService {
|
||||||
@Path(FIRST_BROKER_LOGIN_PATH)
|
@Path(FIRST_BROKER_LOGIN_PATH)
|
||||||
@GET
|
@GET
|
||||||
public Response firstBrokerLoginGet(@QueryParam("code") String code,
|
public Response firstBrokerLoginGet(@QueryParam("code") String code,
|
||||||
@QueryParam("execution") String execution) {
|
@QueryParam("execution") String execution,
|
||||||
return brokerLoginFlow(code, execution, FIRST_BROKER_LOGIN_PATH);
|
@QueryParam("client_id") String clientId) {
|
||||||
|
return brokerLoginFlow(code, execution, clientId, FIRST_BROKER_LOGIN_PATH);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Path(FIRST_BROKER_LOGIN_PATH)
|
@Path(FIRST_BROKER_LOGIN_PATH)
|
||||||
@POST
|
@POST
|
||||||
public Response firstBrokerLoginPost(@QueryParam("code") String code,
|
public Response firstBrokerLoginPost(@QueryParam("code") String code,
|
||||||
@QueryParam("execution") String execution) {
|
@QueryParam("execution") String execution,
|
||||||
return brokerLoginFlow(code, execution, FIRST_BROKER_LOGIN_PATH);
|
@QueryParam("client_id") String clientId) {
|
||||||
|
return brokerLoginFlow(code, execution, clientId, FIRST_BROKER_LOGIN_PATH);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Path(POST_BROKER_LOGIN_PATH)
|
@Path(POST_BROKER_LOGIN_PATH)
|
||||||
@GET
|
@GET
|
||||||
public Response postBrokerLoginGet(@QueryParam("code") String code,
|
public Response postBrokerLoginGet(@QueryParam("code") String code,
|
||||||
@QueryParam("execution") String execution) {
|
@QueryParam("execution") String execution,
|
||||||
return brokerLoginFlow(code, execution, POST_BROKER_LOGIN_PATH);
|
@QueryParam("client_id") String clientId) {
|
||||||
|
return brokerLoginFlow(code, execution, clientId, POST_BROKER_LOGIN_PATH);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Path(POST_BROKER_LOGIN_PATH)
|
@Path(POST_BROKER_LOGIN_PATH)
|
||||||
@POST
|
@POST
|
||||||
public Response postBrokerLoginPost(@QueryParam("code") String code,
|
public Response postBrokerLoginPost(@QueryParam("code") String code,
|
||||||
@QueryParam("execution") String execution) {
|
@QueryParam("execution") String execution,
|
||||||
return brokerLoginFlow(code, execution, POST_BROKER_LOGIN_PATH);
|
@QueryParam("client_id") String clientId) {
|
||||||
|
return brokerLoginFlow(code, execution, clientId, POST_BROKER_LOGIN_PATH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected Response brokerLoginFlow(String code, String execution, String flowPath) {
|
protected Response brokerLoginFlow(String code, String execution, String clientId, String flowPath) {
|
||||||
boolean firstBrokerLogin = flowPath.equals(FIRST_BROKER_LOGIN_PATH);
|
boolean firstBrokerLogin = flowPath.equals(FIRST_BROKER_LOGIN_PATH);
|
||||||
|
|
||||||
EventType eventType = firstBrokerLogin ? EventType.IDENTITY_PROVIDER_FIRST_LOGIN : EventType.IDENTITY_PROVIDER_POST_LOGIN;
|
EventType eventType = firstBrokerLogin ? EventType.IDENTITY_PROVIDER_FIRST_LOGIN : EventType.IDENTITY_PROVIDER_POST_LOGIN;
|
||||||
event.event(eventType);
|
event.event(eventType);
|
||||||
|
|
||||||
SessionCodeChecks checks = checksForCode(code, execution, flowPath);
|
SessionCodeChecks checks = checksForCode(code, execution, clientId, flowPath);
|
||||||
if (!checks.verifyActiveAndValidAction(ClientSessionModel.Action.AUTHENTICATE.name(), ClientSessionCode.ActionType.LOGIN)) {
|
if (!checks.verifyActiveAndValidAction(ClientSessionModel.Action.AUTHENTICATE.name(), ClientSessionCode.ActionType.LOGIN)) {
|
||||||
return checks.getResponse();
|
return checks.getResponse();
|
||||||
}
|
}
|
||||||
|
@ -702,8 +722,9 @@ public class LoginActionsService {
|
||||||
ClientSessionCode<AuthenticationSessionModel> accessCode = new ClientSessionCode<>(session, realm, authSession);
|
ClientSessionCode<AuthenticationSessionModel> accessCode = new ClientSessionCode<>(session, realm, authSession);
|
||||||
authSession.setTimestamp(Time.currentTime());
|
authSession.setTimestamp(Time.currentTime());
|
||||||
|
|
||||||
URI redirect = firstBrokerLogin ? Urls.identityProviderAfterFirstBrokerLogin(uriInfo.getBaseUri(), realm.getName(), accessCode.getCode()) :
|
String clientId = authSession.getClient().getClientId();
|
||||||
Urls.identityProviderAfterPostBrokerLogin(uriInfo.getBaseUri(), realm.getName(), accessCode.getCode()) ;
|
URI redirect = firstBrokerLogin ? Urls.identityProviderAfterFirstBrokerLogin(uriInfo.getBaseUri(), realm.getName(), accessCode.getCode(), clientId) :
|
||||||
|
Urls.identityProviderAfterPostBrokerLogin(uriInfo.getBaseUri(), realm.getName(), accessCode.getCode(), clientId) ;
|
||||||
logger.debugf("Redirecting to '%s' ", redirect);
|
logger.debugf("Redirecting to '%s' ", redirect);
|
||||||
|
|
||||||
return Response.status(302).location(redirect).build();
|
return Response.status(302).location(redirect).build();
|
||||||
|
@ -722,7 +743,8 @@ public class LoginActionsService {
|
||||||
public Response processConsent(final MultivaluedMap<String, String> formData) {
|
public Response processConsent(final MultivaluedMap<String, String> formData) {
|
||||||
event.event(EventType.LOGIN);
|
event.event(EventType.LOGIN);
|
||||||
String code = formData.getFirst("code");
|
String code = formData.getFirst("code");
|
||||||
SessionCodeChecks checks = checksForCode(code, null, REQUIRED_ACTION);
|
String clientId = uriInfo.getQueryParameters().getFirst(Constants.CLIENT_ID);
|
||||||
|
SessionCodeChecks checks = checksForCode(code, null, clientId, REQUIRED_ACTION);
|
||||||
if (!checks.verifyRequiredAction(ClientSessionModel.Action.OAUTH_GRANT.name())) {
|
if (!checks.verifyRequiredAction(ClientSessionModel.Action.OAUTH_GRANT.name())) {
|
||||||
return checks.getResponse();
|
return checks.getResponse();
|
||||||
}
|
}
|
||||||
|
@ -811,21 +833,23 @@ public class LoginActionsService {
|
||||||
@Path(REQUIRED_ACTION)
|
@Path(REQUIRED_ACTION)
|
||||||
@POST
|
@POST
|
||||||
public Response requiredActionPOST(@QueryParam("code") final String code,
|
public Response requiredActionPOST(@QueryParam("code") final String code,
|
||||||
@QueryParam("execution") String action) {
|
@QueryParam("execution") String action,
|
||||||
return processRequireAction(code, action);
|
@QueryParam("client_id") String clientId) {
|
||||||
|
return processRequireAction(code, action, clientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Path(REQUIRED_ACTION)
|
@Path(REQUIRED_ACTION)
|
||||||
@GET
|
@GET
|
||||||
public Response requiredActionGET(@QueryParam("code") final String code,
|
public Response requiredActionGET(@QueryParam("code") final String code,
|
||||||
@QueryParam("execution") String action) {
|
@QueryParam("execution") String action,
|
||||||
return processRequireAction(code, action);
|
@QueryParam("client_id") String clientId) {
|
||||||
|
return processRequireAction(code, action, clientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Response processRequireAction(final String code, String action) {
|
private Response processRequireAction(final String code, String action, String clientId) {
|
||||||
event.event(EventType.CUSTOM_REQUIRED_ACTION);
|
event.event(EventType.CUSTOM_REQUIRED_ACTION);
|
||||||
|
|
||||||
SessionCodeChecks checks = checksForCode(code, action, REQUIRED_ACTION);
|
SessionCodeChecks checks = checksForCode(code, action, clientId, REQUIRED_ACTION);
|
||||||
if (!checks.verifyRequiredAction(action)) {
|
if (!checks.verifyRequiredAction(action)) {
|
||||||
return checks.getResponse();
|
return checks.getResponse();
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,16 +120,8 @@ public class LoginActionsServiceChecks {
|
||||||
LoginFormsProvider loginForm = context.getSession().getProvider(LoginFormsProvider.class)
|
LoginFormsProvider loginForm = context.getSession().getProvider(LoginFormsProvider.class)
|
||||||
.setSuccess(Messages.ALREADY_LOGGED_IN);
|
.setSuccess(Messages.ALREADY_LOGGED_IN);
|
||||||
|
|
||||||
ClientModel client = null;
|
if (context.getSession().getContext().getClient() == null) {
|
||||||
String lastClientUuid = userSession.getNote(AuthenticationManager.LAST_AUTHENTICATED_CLIENT);
|
loginForm.setAttribute(Constants.SKIP_LINK, true);
|
||||||
if (lastClientUuid != null) {
|
|
||||||
client = context.getRealm().getClientById(lastClientUuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (client != null) {
|
|
||||||
context.getSession().getContext().setClient(client);
|
|
||||||
} else {
|
|
||||||
loginForm.setAttribute("skipLink", true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new LoginActionsServiceException(loginForm.createInfoPage());
|
throw new LoginActionsServiceException(loginForm.createInfoPage());
|
||||||
|
|
|
@ -33,6 +33,7 @@ import org.keycloak.events.EventBuilder;
|
||||||
import org.keycloak.forms.login.LoginFormsProvider;
|
import org.keycloak.forms.login.LoginFormsProvider;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.ClientSessionModel;
|
import org.keycloak.models.ClientSessionModel;
|
||||||
|
import org.keycloak.models.Constants;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserSessionModel;
|
import org.keycloak.models.UserSessionModel;
|
||||||
|
@ -66,10 +67,11 @@ public class SessionCodeChecks {
|
||||||
|
|
||||||
private final String code;
|
private final String code;
|
||||||
private final String execution;
|
private final String execution;
|
||||||
|
private final String clientId;
|
||||||
private final String flowPath;
|
private final String flowPath;
|
||||||
|
|
||||||
|
|
||||||
public SessionCodeChecks(RealmModel realm, UriInfo uriInfo, ClientConnection clientConnection, KeycloakSession session, EventBuilder event, String code, String execution, String flowPath) {
|
public SessionCodeChecks(RealmModel realm, UriInfo uriInfo, ClientConnection clientConnection, KeycloakSession session, EventBuilder event, String code, String execution, String clientId, String flowPath) {
|
||||||
this.realm = realm;
|
this.realm = realm;
|
||||||
this.uriInfo = uriInfo;
|
this.uriInfo = uriInfo;
|
||||||
this.clientConnection = clientConnection;
|
this.clientConnection = clientConnection;
|
||||||
|
@ -78,6 +80,7 @@ public class SessionCodeChecks {
|
||||||
|
|
||||||
this.code = code;
|
this.code = code;
|
||||||
this.execution = execution;
|
this.execution = execution;
|
||||||
|
this.clientId = clientId;
|
||||||
this.flowPath = flowPath;
|
this.flowPath = flowPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,6 +137,16 @@ public class SessionCodeChecks {
|
||||||
return authSession;
|
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;
|
||||||
|
if (clientId != null) {
|
||||||
|
client = realm.getClientByClientId(clientId);
|
||||||
|
}
|
||||||
|
if (client != null) {
|
||||||
|
session.getContext().setClient(client);
|
||||||
|
}
|
||||||
|
|
||||||
// See if we are already authenticated and userSession with same ID exists.
|
// See if we are already authenticated and userSession with same ID exists.
|
||||||
String sessionId = new AuthenticationSessionManager(session).getCurrentAuthenticationSessionId(realm);
|
String sessionId = new AuthenticationSessionManager(session).getCurrentAuthenticationSessionId(realm);
|
||||||
if (sessionId != null) {
|
if (sessionId != null) {
|
||||||
|
@ -143,16 +156,8 @@ public class SessionCodeChecks {
|
||||||
LoginFormsProvider loginForm = session.getProvider(LoginFormsProvider.class)
|
LoginFormsProvider loginForm = session.getProvider(LoginFormsProvider.class)
|
||||||
.setSuccess(Messages.ALREADY_LOGGED_IN);
|
.setSuccess(Messages.ALREADY_LOGGED_IN);
|
||||||
|
|
||||||
ClientModel client = null;
|
if (client == null) {
|
||||||
String lastClientUuid = userSession.getNote(AuthenticationManager.LAST_AUTHENTICATED_CLIENT);
|
loginForm.setAttribute(Constants.SKIP_LINK, true);
|
||||||
if (lastClientUuid != null) {
|
|
||||||
client = realm.getClientById(lastClientUuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (client != null) {
|
|
||||||
session.getContext().setClient(client);
|
|
||||||
} else {
|
|
||||||
loginForm.setAttribute("skipLink", true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
response = loginForm.createInfoPage();
|
response = loginForm.createInfoPage();
|
||||||
|
@ -234,7 +239,7 @@ public class SessionCodeChecks {
|
||||||
// In case that is replayed action, but sent to the same FORM like actual FORM, we just re-render the page
|
// In case that is replayed action, but sent to the same FORM like actual FORM, we just re-render the page
|
||||||
if (ObjectUtil.isEqualOrBothNull(execution, authSession.getAuthNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION))) {
|
if (ObjectUtil.isEqualOrBothNull(execution, authSession.getAuthNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION))) {
|
||||||
String latestFlowPath = authSession.getAuthNote(AuthenticationProcessor.CURRENT_FLOW_PATH);
|
String latestFlowPath = authSession.getAuthNote(AuthenticationProcessor.CURRENT_FLOW_PATH);
|
||||||
URI redirectUri = getLastExecutionUrl(latestFlowPath, execution);
|
URI redirectUri = getLastExecutionUrl(latestFlowPath, execution, client.getClientId());
|
||||||
|
|
||||||
logger.debugf("Invalid action code, but execution matches. So just redirecting to %s", redirectUri);
|
logger.debugf("Invalid action code, but execution matches. So just redirecting to %s", redirectUri);
|
||||||
authSession.setAuthNote(LoginActionsService.FORWARDED_ERROR_MESSAGE_NOTE, Messages.EXPIRED_ACTION);
|
authSession.setAuthNote(LoginActionsService.FORWARDED_ERROR_MESSAGE_NOTE, Messages.EXPIRED_ACTION);
|
||||||
|
@ -289,7 +294,7 @@ public class SessionCodeChecks {
|
||||||
|
|
||||||
authSession.setAuthNote(LoginActionsService.FORWARDED_ERROR_MESSAGE_NOTE, Messages.LOGIN_TIMEOUT);
|
authSession.setAuthNote(LoginActionsService.FORWARDED_ERROR_MESSAGE_NOTE, Messages.LOGIN_TIMEOUT);
|
||||||
|
|
||||||
URI redirectUri = getLastExecutionUrl(LoginActionsService.AUTHENTICATE_PATH, null);
|
URI redirectUri = getLastExecutionUrl(LoginActionsService.AUTHENTICATE_PATH, null, authSession.getClient().getClientId());
|
||||||
logger.debugf("Flow restart after timeout. Redirecting to %s", redirectUri);
|
logger.debugf("Flow restart after timeout. Redirecting to %s", redirectUri);
|
||||||
response = Response.status(Response.Status.FOUND).location(redirectUri).build();
|
response = Response.status(Response.Status.FOUND).location(redirectUri).build();
|
||||||
return false;
|
return false;
|
||||||
|
@ -351,7 +356,7 @@ public class SessionCodeChecks {
|
||||||
flowPath = LoginActionsService.AUTHENTICATE_PATH;
|
flowPath = LoginActionsService.AUTHENTICATE_PATH;
|
||||||
}
|
}
|
||||||
|
|
||||||
URI redirectUri = getLastExecutionUrl(flowPath, null);
|
URI redirectUri = getLastExecutionUrl(flowPath, null, authSession.getClient().getClientId());
|
||||||
logger.debugf("Authentication session restart from cookie succeeded. Redirecting to %s", redirectUri);
|
logger.debugf("Authentication session restart from cookie succeeded. Redirecting to %s", redirectUri);
|
||||||
return Response.status(Response.Status.FOUND).location(redirectUri).build();
|
return Response.status(Response.Status.FOUND).location(redirectUri).build();
|
||||||
} else {
|
} else {
|
||||||
|
@ -367,16 +372,20 @@ public class SessionCodeChecks {
|
||||||
.path(LoginActionsService.REQUIRED_ACTION);
|
.path(LoginActionsService.REQUIRED_ACTION);
|
||||||
|
|
||||||
if (action != null) {
|
if (action != null) {
|
||||||
uriBuilder.queryParam("execution", action);
|
uriBuilder.queryParam(Constants.EXECUTION, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ClientModel client = authSession.getClient();
|
||||||
|
uriBuilder.queryParam(Constants.CLIENT_ID, client.getClientId());
|
||||||
|
|
||||||
URI redirect = uriBuilder.build(realm.getName());
|
URI redirect = uriBuilder.build(realm.getName());
|
||||||
return Response.status(302).location(redirect).build();
|
return Response.status(302).location(redirect).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private URI getLastExecutionUrl(String flowPath, String executionId) {
|
private URI getLastExecutionUrl(String flowPath, String executionId, String clientId) {
|
||||||
return new AuthenticationFlowURLHelper(session, realm, uriInfo)
|
return new AuthenticationFlowURLHelper(session, realm, uriInfo)
|
||||||
.getLastExecutionUrl(flowPath, executionId);
|
.getLastExecutionUrl(flowPath, executionId, clientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ import javax.ws.rs.core.UriInfo;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.authentication.AuthenticationProcessor;
|
import org.keycloak.authentication.AuthenticationProcessor;
|
||||||
import org.keycloak.forms.login.LoginFormsProvider;
|
import org.keycloak.forms.login.LoginFormsProvider;
|
||||||
|
import org.keycloak.models.Constants;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.protocol.AuthorizationEndpointBase;
|
import org.keycloak.protocol.AuthorizationEndpointBase;
|
||||||
|
@ -61,13 +62,15 @@ public class AuthenticationFlowURLHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public URI getLastExecutionUrl(String flowPath, String executionId) {
|
public URI getLastExecutionUrl(String flowPath, String executionId, String clientId) {
|
||||||
UriBuilder uriBuilder = LoginActionsService.loginActionsBaseUrl(uriInfo)
|
UriBuilder uriBuilder = LoginActionsService.loginActionsBaseUrl(uriInfo)
|
||||||
.path(flowPath);
|
.path(flowPath);
|
||||||
|
|
||||||
if (executionId != null) {
|
if (executionId != null) {
|
||||||
uriBuilder.queryParam("execution", executionId);
|
uriBuilder.queryParam(Constants.EXECUTION, executionId);
|
||||||
}
|
}
|
||||||
|
uriBuilder.queryParam(Constants.CLIENT_ID, clientId);
|
||||||
|
|
||||||
return uriBuilder.build(realm.getName());
|
return uriBuilder.build(realm.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,7 +87,7 @@ public class AuthenticationFlowURLHelper {
|
||||||
latestFlowPath = LoginActionsService.AUTHENTICATE_PATH;
|
latestFlowPath = LoginActionsService.AUTHENTICATE_PATH;
|
||||||
}
|
}
|
||||||
|
|
||||||
return getLastExecutionUrl(latestFlowPath, executionId);
|
return getLastExecutionUrl(latestFlowPath, executionId, authSession.getClient().getClientId());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,7 @@ public class TwitterIdentityProvider extends AbstractIdentityProvider<OAuth2Iden
|
||||||
Twitter twitter = new TwitterFactory().getInstance();
|
Twitter twitter = new TwitterFactory().getInstance();
|
||||||
twitter.setOAuthConsumer(getConfig().getClientId(), getConfig().getClientSecret());
|
twitter.setOAuthConsumer(getConfig().getClientId(), getConfig().getClientSecret());
|
||||||
|
|
||||||
URI uri = new URI(request.getRedirectUri() + "?state=" + request.getState());
|
URI uri = new URI(request.getRedirectUri() + "?state=" + request.getState().getEncodedState());
|
||||||
|
|
||||||
RequestToken requestToken = twitter.getOAuthRequestToken(uri.toString());
|
RequestToken requestToken = twitter.getOAuthRequestToken(uri.toString());
|
||||||
AuthenticationSessionModel authSession = request.getAuthenticationSession();
|
AuthenticationSessionModel authSession = request.getAuthenticationSession();
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.keycloak.testsuite;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.UriBuilder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper for parse action-uri from the HTML login page and do something with it (eg. open in new browser, parse code parameter and use it somewhere else etc)
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
|
*/
|
||||||
|
public class ActionURIUtils {
|
||||||
|
|
||||||
|
private static final Pattern ACTION_URI_PATTERN = Pattern.compile("action=\"([^\"]+)\"");
|
||||||
|
|
||||||
|
private static final Pattern QUERY_STRING_PATTERN = Pattern.compile("[^\\?]+\\?([^#]+).*");
|
||||||
|
|
||||||
|
private static final Pattern PARAMS_PATTERN = Pattern.compile("[=\\&]");
|
||||||
|
|
||||||
|
public static String getActionURIFromPageSource(String htmlPageSource) {
|
||||||
|
Matcher m = ACTION_URI_PATTERN.matcher(htmlPageSource);
|
||||||
|
if (m.find()) {
|
||||||
|
return m.group(1).replaceAll("&", "&");
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, String> parseQueryParamsFromActionURI(String actionURI) {
|
||||||
|
Matcher m = QUERY_STRING_PATTERN.matcher(actionURI);
|
||||||
|
if (m.find()) {
|
||||||
|
String queryString = m.group(1);
|
||||||
|
|
||||||
|
String[] params = PARAMS_PATTERN.split(queryString, 0);
|
||||||
|
Map<String, String> result = new HashMap<>(); // Don't take multivalued into account for now
|
||||||
|
|
||||||
|
for (int i=0 ; i<params.length ; i+=2) {
|
||||||
|
String paramName = params[i];
|
||||||
|
String paramValue = params[i+1];
|
||||||
|
result.put(paramName, paramValue);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String removeQueryParamFromURI(String actionURI, String paramName) {
|
||||||
|
return UriBuilder.fromUri(actionURI)
|
||||||
|
.replaceQueryParam(paramName, null)
|
||||||
|
.build().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
private static final String TEST = "<form id=\"kc-form-login\" class=\"form-horizontal\" action=\"http://localhost:8180/auth/realms/child/login-actions/authenticate?code=1WnqOmapgo0cj3mpRQ-vbleIKUJdwFzonzy1fjvnWQQ&execution=3ac92a20-9c31-49de-a3c8-f2a4fff80986&client_id=client-linking\" method=\"post\">";
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
String actionURI = getActionURIFromPageSource(TEST);
|
||||||
|
System.out.println("action uri: " + actionURI);
|
||||||
|
|
||||||
|
Map<String, String> params = parseQueryParamsFromActionURI(actionURI);
|
||||||
|
System.out.println("params: " + params);
|
||||||
|
|
||||||
|
String actionURI2 = removeQueryParamFromURI(actionURI, "execution");
|
||||||
|
System.out.println("action uri 2: " + actionURI2);
|
||||||
|
}*/
|
||||||
|
}
|
|
@ -43,6 +43,14 @@ public class ErrorPage extends AbstractPage {
|
||||||
backToApplicationLink.click();
|
backToApplicationLink.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getBackToApplicationLink() {
|
||||||
|
if (backToApplicationLink == null) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return backToApplicationLink.getAttribute("href");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isCurrent() {
|
public boolean isCurrent() {
|
||||||
return driver.getTitle() != null && driver.getTitle().equals("We're sorry...");
|
return driver.getTitle() != null && driver.getTitle().equals("We're sorry...");
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ import org.junit.Test;
|
||||||
import org.keycloak.events.Details;
|
import org.keycloak.events.Details;
|
||||||
import org.keycloak.events.EventType;
|
import org.keycloak.events.EventType;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
import org.keycloak.testsuite.AssertEvents;
|
import org.keycloak.testsuite.AssertEvents;
|
||||||
|
@ -32,6 +33,7 @@ import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
||||||
import org.keycloak.testsuite.admin.ApiUtil;
|
import org.keycloak.testsuite.admin.ApiUtil;
|
||||||
import org.keycloak.testsuite.pages.AppPage;
|
import org.keycloak.testsuite.pages.AppPage;
|
||||||
import org.keycloak.testsuite.pages.AppPage.RequestType;
|
import org.keycloak.testsuite.pages.AppPage.RequestType;
|
||||||
|
import org.keycloak.testsuite.pages.ErrorPage;
|
||||||
import org.keycloak.testsuite.pages.LoginPage;
|
import org.keycloak.testsuite.pages.LoginPage;
|
||||||
import org.keycloak.testsuite.pages.LoginUpdateProfileEditUsernameAllowedPage;
|
import org.keycloak.testsuite.pages.LoginUpdateProfileEditUsernameAllowedPage;
|
||||||
import org.keycloak.testsuite.util.UserBuilder;
|
import org.keycloak.testsuite.util.UserBuilder;
|
||||||
|
@ -53,6 +55,9 @@ public class RequiredActionUpdateProfileTest extends AbstractTestRealmKeycloakTe
|
||||||
@Page
|
@Page
|
||||||
protected LoginUpdateProfileEditUsernameAllowedPage updateProfilePage;
|
protected LoginUpdateProfileEditUsernameAllowedPage updateProfilePage;
|
||||||
|
|
||||||
|
@Page
|
||||||
|
protected ErrorPage errorPage;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||||
ActionUtil.addRequiredActionForUser(testRealm, "test-user@localhost", UserModel.RequiredAction.UPDATE_PROFILE.name());
|
ActionUtil.addRequiredActionForUser(testRealm, "test-user@localhost", UserModel.RequiredAction.UPDATE_PROFILE.name());
|
||||||
|
@ -294,4 +299,23 @@ public class RequiredActionUpdateProfileTest extends AbstractTestRealmKeycloakTe
|
||||||
events.assertEmpty();
|
events.assertEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updateProfileExpiredCookies() {
|
||||||
|
loginPage.open();
|
||||||
|
loginPage.login("john-doh@localhost", "password");
|
||||||
|
|
||||||
|
updateProfilePage.assertCurrent();
|
||||||
|
|
||||||
|
// Expire cookies and assert the page with "back to application" link present
|
||||||
|
driver.manage().deleteAllCookies();
|
||||||
|
|
||||||
|
updateProfilePage.update("New first", "New last", "keycloak-user@localhost", "test-user@localhost");
|
||||||
|
errorPage.assertCurrent();
|
||||||
|
|
||||||
|
String backToAppLink = errorPage.getBackToApplicationLink();
|
||||||
|
|
||||||
|
ClientRepresentation client = ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app").toRepresentation();
|
||||||
|
Assert.assertEquals(backToAppLink, client.getBaseUrl());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.jboss.shrinkwrap.api.spec.WebArchive;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.keycloak.OAuth2Constants;
|
||||||
import org.keycloak.admin.client.resource.ClientResource;
|
import org.keycloak.admin.client.resource.ClientResource;
|
||||||
import org.keycloak.admin.client.resource.RealmResource;
|
import org.keycloak.admin.client.resource.RealmResource;
|
||||||
import org.keycloak.common.util.Base64Url;
|
import org.keycloak.common.util.Base64Url;
|
||||||
|
@ -37,6 +38,7 @@ import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.representations.idm.RoleRepresentation;
|
import org.keycloak.representations.idm.RoleRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
|
import org.keycloak.testsuite.ActionURIUtils;
|
||||||
import org.keycloak.testsuite.adapter.AbstractServletsAdapterTest;
|
import org.keycloak.testsuite.adapter.AbstractServletsAdapterTest;
|
||||||
import org.keycloak.testsuite.admin.ApiUtil;
|
import org.keycloak.testsuite.admin.ApiUtil;
|
||||||
import org.keycloak.testsuite.arquillian.AuthServerTestEnricher;
|
import org.keycloak.testsuite.arquillian.AuthServerTestEnricher;
|
||||||
|
@ -57,6 +59,7 @@ import javax.ws.rs.core.UriBuilder;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
@ -496,23 +499,11 @@ public abstract class AbstractClientInitiatedAccountLinkTest extends AbstractSer
|
||||||
|
|
||||||
// ok, now scrape the code from page
|
// ok, now scrape the code from page
|
||||||
String pageSource = driver.getPageSource();
|
String pageSource = driver.getPageSource();
|
||||||
Pattern p = Pattern.compile("action=\"(.+)\"");
|
String action = ActionURIUtils.getActionURIFromPageSource(pageSource);
|
||||||
Matcher m = p.matcher(pageSource);
|
System.out.println("action uri: " + action);
|
||||||
String action = null;
|
|
||||||
if (m.find()) {
|
|
||||||
action = m.group(1);
|
|
||||||
|
|
||||||
}
|
Map<String, String> queryParams = ActionURIUtils.parseQueryParamsFromActionURI(action);
|
||||||
System.out.println("action: " + action);
|
System.out.println("query params: " + queryParams);
|
||||||
|
|
||||||
p = Pattern.compile("code=(.+)&");
|
|
||||||
m = p.matcher(action);
|
|
||||||
String code = null;
|
|
||||||
if (m.find()) {
|
|
||||||
code = m.group(1);
|
|
||||||
|
|
||||||
}
|
|
||||||
System.out.println("code: " + code);
|
|
||||||
|
|
||||||
// now try and use the code to login to remote link-only idp
|
// now try and use the code to login to remote link-only idp
|
||||||
|
|
||||||
|
@ -520,7 +511,8 @@ public abstract class AbstractClientInitiatedAccountLinkTest extends AbstractSer
|
||||||
|
|
||||||
uri = UriBuilder.fromUri(AuthServerTestEnricher.getAuthServerContextRoot())
|
uri = UriBuilder.fromUri(AuthServerTestEnricher.getAuthServerContextRoot())
|
||||||
.path(uri)
|
.path(uri)
|
||||||
.queryParam("code", code)
|
.queryParam(OAuth2Constants.CODE, queryParams.get(OAuth2Constants.CODE))
|
||||||
|
.queryParam(Constants.CLIENT_ID, queryParams.get(Constants.CLIENT_ID))
|
||||||
.build().toString();
|
.build().toString();
|
||||||
|
|
||||||
System.out.println("hack uri: " + uri);
|
System.out.println("hack uri: " + uri);
|
||||||
|
|
|
@ -387,4 +387,22 @@ public abstract class AbstractBrokerTest extends AbstractBaseBrokerTest {
|
||||||
logoutFromRealm(bc.providerRealmName());
|
logoutFromRealm(bc.providerRealmName());
|
||||||
logoutFromRealm(bc.consumerRealmName());
|
logoutFromRealm(bc.consumerRealmName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// KEYCLOAK-4016
|
||||||
|
@Test
|
||||||
|
public void testExpiredCode() {
|
||||||
|
driver.navigate().to(getAccountUrl(bc.consumerRealmName()));
|
||||||
|
|
||||||
|
log.debug("Expire all browser cookies");
|
||||||
|
driver.manage().deleteAllCookies();
|
||||||
|
|
||||||
|
log.debug("Clicking social " + bc.getIDPAlias());
|
||||||
|
accountLoginPage.clickSocial(bc.getIDPAlias());
|
||||||
|
|
||||||
|
waitForPage(driver, "sorry");
|
||||||
|
errorPage.assertCurrent();
|
||||||
|
String link = errorPage.getBackToApplicationLink();
|
||||||
|
Assert.assertTrue(link.endsWith("/auth/realms/consumer/account"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,7 @@ import org.keycloak.representations.idm.ComponentRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
import org.keycloak.storage.UserStorageProvider;
|
import org.keycloak.storage.UserStorageProvider;
|
||||||
import org.keycloak.storage.UserStorageProviderModel;
|
import org.keycloak.storage.UserStorageProviderModel;
|
||||||
|
import org.keycloak.testsuite.ActionURIUtils;
|
||||||
import org.keycloak.testsuite.util.KerberosRule;
|
import org.keycloak.testsuite.util.KerberosRule;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -156,10 +157,7 @@ public class KerberosStandaloneTest extends AbstractKerberosTest {
|
||||||
|
|
||||||
Assert.assertTrue(context.contains("Log in to test"));
|
Assert.assertTrue(context.contains("Log in to test"));
|
||||||
|
|
||||||
Pattern pattern = Pattern.compile("action=\"([^\"]+)\"");
|
String url = ActionURIUtils.getActionURIFromPageSource(context);
|
||||||
Matcher m = pattern.matcher(context);
|
|
||||||
Assert.assertTrue(m.find());
|
|
||||||
String url = m.group(1);
|
|
||||||
|
|
||||||
|
|
||||||
// Follow login with HttpClient. Improve if needed
|
// Follow login with HttpClient. Improve if needed
|
||||||
|
|
|
@ -27,6 +27,7 @@ import org.keycloak.events.Errors;
|
||||||
import org.keycloak.events.EventType;
|
import org.keycloak.events.EventType;
|
||||||
import org.keycloak.models.BrowserSecurityHeaders;
|
import org.keycloak.models.BrowserSecurityHeaders;
|
||||||
import org.keycloak.models.Constants;
|
import org.keycloak.models.Constants;
|
||||||
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.representations.idm.EventRepresentation;
|
import org.keycloak.representations.idm.EventRepresentation;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
|
@ -54,6 +55,7 @@ import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotEquals;
|
import static org.junit.Assert.assertNotEquals;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.keycloak.testsuite.admin.ApiUtil.findClientByClientId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
@ -600,9 +602,13 @@ public class LoginTest extends AbstractTestRealmKeycloakTest {
|
||||||
|
|
||||||
driver.manage().deleteAllCookies();
|
driver.manage().deleteAllCookies();
|
||||||
|
|
||||||
// Cookies are expired including KC_RESTART. No way to continue login. Error page must be shown
|
// Cookies are expired including KC_RESTART. No way to continue login. Error page must be shown with the "back to application" link
|
||||||
loginPage.login("login@test.com", "password");
|
loginPage.login("login@test.com", "password");
|
||||||
errorPage.assertCurrent();
|
errorPage.assertCurrent();
|
||||||
|
String link = errorPage.getBackToApplicationLink();
|
||||||
|
|
||||||
|
ClientRepresentation thirdParty = findClientByClientId(adminClient.realm("test"), "third-party").toRepresentation();
|
||||||
|
Assert.assertNotNull(link, thirdParty.getBaseUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
||||||
|
import org.keycloak.testsuite.ActionURIUtils;
|
||||||
import org.keycloak.testsuite.Assert;
|
import org.keycloak.testsuite.Assert;
|
||||||
import org.keycloak.testsuite.AssertEvents;
|
import org.keycloak.testsuite.AssertEvents;
|
||||||
import org.keycloak.testsuite.admin.ApiUtil;
|
import org.keycloak.testsuite.admin.ApiUtil;
|
||||||
|
@ -120,22 +121,16 @@ public class MultipleTabsLoginTest extends AbstractTestRealmKeycloakTest {
|
||||||
public void openMultipleTabs() {
|
public void openMultipleTabs() {
|
||||||
oauth.openLoginForm();
|
oauth.openLoginForm();
|
||||||
loginPage.assertCurrent();
|
loginPage.assertCurrent();
|
||||||
String actionUrl1 = getActionUrl(driver.getPageSource());
|
String actionUrl1 = ActionURIUtils.getActionURIFromPageSource(driver.getPageSource());
|
||||||
|
|
||||||
oauth.openLoginForm();
|
oauth.openLoginForm();
|
||||||
loginPage.assertCurrent();
|
loginPage.assertCurrent();
|
||||||
String actionUrl2 = getActionUrl(driver.getPageSource());
|
String actionUrl2 = ActionURIUtils.getActionURIFromPageSource(driver.getPageSource());
|
||||||
|
|
||||||
Assert.assertEquals(actionUrl1, actionUrl2);
|
Assert.assertEquals(actionUrl1, actionUrl2);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private String getActionUrl(String pageSource) {
|
|
||||||
return pageSource.split("action=\"")[1].split("\"")[0].replaceAll("&", "&");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void multipleTabsParallelLoginTest() {
|
public void multipleTabsParallelLoginTest() {
|
||||||
oauth.openLoginForm();
|
oauth.openLoginForm();
|
||||||
|
@ -173,7 +168,7 @@ public class MultipleTabsLoginTest extends AbstractTestRealmKeycloakTest {
|
||||||
// Simulate to open login form in 2 tabs
|
// Simulate to open login form in 2 tabs
|
||||||
oauth.openLoginForm();
|
oauth.openLoginForm();
|
||||||
loginPage.assertCurrent();
|
loginPage.assertCurrent();
|
||||||
String actionUrl1 = getActionUrl(driver.getPageSource());
|
String actionUrl1 = ActionURIUtils.getActionURIFromPageSource(driver.getPageSource());
|
||||||
|
|
||||||
// Click "register" in tab2
|
// Click "register" in tab2
|
||||||
loginPage.clickRegister();
|
loginPage.clickRegister();
|
||||||
|
@ -204,7 +199,7 @@ public class MultipleTabsLoginTest extends AbstractTestRealmKeycloakTest {
|
||||||
// Simulate to open login form in 2 tabs
|
// Simulate to open login form in 2 tabs
|
||||||
oauth.openLoginForm();
|
oauth.openLoginForm();
|
||||||
loginPage.assertCurrent();
|
loginPage.assertCurrent();
|
||||||
String actionUrl1 = getActionUrl(driver.getPageSource());
|
String actionUrl1 = ActionURIUtils.getActionURIFromPageSource(driver.getPageSource());
|
||||||
|
|
||||||
loginPage.login("invalid", "invalid");
|
loginPage.login("invalid", "invalid");
|
||||||
loginPage.assertCurrent();
|
loginPage.assertCurrent();
|
||||||
|
@ -228,7 +223,7 @@ public class MultipleTabsLoginTest extends AbstractTestRealmKeycloakTest {
|
||||||
// Open tab1
|
// Open tab1
|
||||||
oauth.openLoginForm();
|
oauth.openLoginForm();
|
||||||
loginPage.assertCurrent();
|
loginPage.assertCurrent();
|
||||||
String actionUrl1 = getActionUrl(driver.getPageSource());
|
String actionUrl1 = ActionURIUtils.getActionURIFromPageSource(driver.getPageSource());
|
||||||
|
|
||||||
// Authenticate in tab2
|
// Authenticate in tab2
|
||||||
loginPage.login("login-test", "password");
|
loginPage.login("login-test", "password");
|
||||||
|
@ -253,8 +248,8 @@ public class MultipleTabsLoginTest extends AbstractTestRealmKeycloakTest {
|
||||||
oauth.openLoginForm();
|
oauth.openLoginForm();
|
||||||
|
|
||||||
// Manually remove execution from the URL and try to simulate the request just with "code" parameter
|
// Manually remove execution from the URL and try to simulate the request just with "code" parameter
|
||||||
String actionUrl = driver.getPageSource().split("action=\"")[1].split("\"")[0].replaceAll("&", "&");
|
String actionUrl = ActionURIUtils.getActionURIFromPageSource(driver.getPageSource());
|
||||||
actionUrl = actionUrl.replaceFirst("&execution=.*", "");
|
actionUrl = ActionURIUtils.removeQueryParamFromURI(actionUrl, Constants.EXECUTION);
|
||||||
|
|
||||||
driver.navigate().to(actionUrl);
|
driver.navigate().to(actionUrl);
|
||||||
|
|
||||||
|
@ -272,8 +267,8 @@ public class MultipleTabsLoginTest extends AbstractTestRealmKeycloakTest {
|
||||||
updatePasswordPage.assertCurrent();
|
updatePasswordPage.assertCurrent();
|
||||||
|
|
||||||
// Manually remove execution from the URL and try to simulate the request just with "code" parameter
|
// Manually remove execution from the URL and try to simulate the request just with "code" parameter
|
||||||
String actionUrl = driver.getPageSource().split("action=\"")[1].split("\"")[0].replaceAll("&", "&");
|
String actionUrl = ActionURIUtils.getActionURIFromPageSource(driver.getPageSource());
|
||||||
actionUrl = actionUrl.replaceFirst("&execution=.*", "");
|
actionUrl = ActionURIUtils.removeQueryParamFromURI(actionUrl, Constants.EXECUTION);
|
||||||
|
|
||||||
driver.navigate().to(actionUrl);
|
driver.navigate().to(actionUrl);
|
||||||
|
|
||||||
|
|
|
@ -418,6 +418,51 @@ public class ResetPasswordTest extends AbstractTestRealmKeycloakTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// KEYCLOAK-4016
|
||||||
|
@Test
|
||||||
|
public void resetPasswordExpiredCodeAndAuthSession() throws IOException, MessagingException, InterruptedException {
|
||||||
|
final AtomicInteger originalValue = new AtomicInteger();
|
||||||
|
|
||||||
|
RealmRepresentation realmRep = testRealm().toRepresentation();
|
||||||
|
originalValue.set(realmRep.getActionTokenGeneratedByUserLifespan());
|
||||||
|
realmRep.setActionTokenGeneratedByUserLifespan(60);
|
||||||
|
testRealm().update(realmRep);
|
||||||
|
|
||||||
|
try {
|
||||||
|
initiateResetPasswordFromResetPasswordPage("login-test");
|
||||||
|
|
||||||
|
events.expectRequiredAction(EventType.SEND_RESET_PASSWORD)
|
||||||
|
.session((String)null)
|
||||||
|
.user(userId).detail(Details.USERNAME, "login-test").detail(Details.EMAIL, "login@test.com").assertEvent();
|
||||||
|
|
||||||
|
assertEquals(1, greenMail.getReceivedMessages().length);
|
||||||
|
|
||||||
|
MimeMessage message = greenMail.getReceivedMessages()[0];
|
||||||
|
|
||||||
|
String changePasswordUrl = getPasswordResetEmailLink(message);
|
||||||
|
|
||||||
|
setTimeOffset(70);
|
||||||
|
|
||||||
|
log.debug("Going to reset password URI.");
|
||||||
|
driver.navigate().to(oauth.AUTH_SERVER_ROOT + "/realms/test/login-actions/reset-credentials"); // This is necessary to delete KC_RESTART cookie that is restricted to /auth/realms/test path
|
||||||
|
log.debug("Removing cookies.");
|
||||||
|
driver.manage().deleteAllCookies();
|
||||||
|
driver.navigate().to(changePasswordUrl.trim());
|
||||||
|
|
||||||
|
errorPage.assertCurrent();
|
||||||
|
Assert.assertEquals("Reset Credential not allowed", errorPage.getError());
|
||||||
|
String backToAppLink = errorPage.getBackToApplicationLink();
|
||||||
|
Assert.assertTrue(backToAppLink.endsWith("/app/auth"));
|
||||||
|
|
||||||
|
events.expectRequiredAction(EventType.EXECUTE_ACTION_TOKEN_ERROR).error("expired_code").client((String) null).user(userId).session((String) null).clearDetails().detail(Details.ACTION, ResetCredentialsActionToken.TOKEN_TYPE).assertEvent();
|
||||||
|
} finally {
|
||||||
|
setTimeOffset(0);
|
||||||
|
|
||||||
|
realmRep.setActionTokenGeneratedByUserLifespan(originalValue.get());
|
||||||
|
testRealm().update(realmRep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void resetPasswordDisabledUser() throws IOException, MessagingException, InterruptedException {
|
public void resetPasswordDisabledUser() throws IOException, MessagingException, InterruptedException {
|
||||||
UserRepresentation user = findUser("login-test");
|
UserRepresentation user = findUser("login-test");
|
||||||
|
|
|
@ -56,6 +56,7 @@ import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.representations.idm.RoleRepresentation;
|
import org.keycloak.representations.idm.RoleRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
import org.keycloak.testsuite.AbstractKeycloakTest;
|
import org.keycloak.testsuite.AbstractKeycloakTest;
|
||||||
|
import org.keycloak.testsuite.ActionURIUtils;
|
||||||
import org.keycloak.testsuite.AssertEvents;
|
import org.keycloak.testsuite.AssertEvents;
|
||||||
import org.keycloak.testsuite.arquillian.AuthServerTestEnricher;
|
import org.keycloak.testsuite.arquillian.AuthServerTestEnricher;
|
||||||
import org.keycloak.testsuite.util.ClientBuilder;
|
import org.keycloak.testsuite.util.ClientBuilder;
|
||||||
|
@ -210,7 +211,8 @@ public class AccessTokenTest extends AbstractKeycloakTest {
|
||||||
oauth.redirectUri(AuthServerTestEnricher.getAuthServerContextRoot() + "/auth/admin/test/console/nosuch.html");
|
oauth.redirectUri(AuthServerTestEnricher.getAuthServerContextRoot() + "/auth/admin/test/console/nosuch.html");
|
||||||
oauth.openLoginForm();
|
oauth.openLoginForm();
|
||||||
|
|
||||||
String loginPageCode = driver.getPageSource().split("code=")[1].split("&")[0].split("\"")[0];
|
String actionURI = ActionURIUtils.getActionURIFromPageSource(driver.getPageSource());
|
||||||
|
String loginPageCode = ActionURIUtils.parseQueryParamsFromActionURI(actionURI).get("code");
|
||||||
|
|
||||||
oauth.fillLoginForm("test-user@localhost", "password");
|
oauth.fillLoginForm("test-user@localhost", "password");
|
||||||
|
|
||||||
|
@ -441,7 +443,8 @@ public class AccessTokenTest extends AbstractKeycloakTest {
|
||||||
|
|
||||||
oauth.doLogin("test-user@localhost", "password");
|
oauth.doLogin("test-user@localhost", "password");
|
||||||
|
|
||||||
String code = driver.getPageSource().split("code=")[1].split("&")[0].split("\"")[0];
|
String actionURI = ActionURIUtils.getActionURIFromPageSource(driver.getPageSource());
|
||||||
|
String code = ActionURIUtils.parseQueryParamsFromActionURI(actionURI).get("code");
|
||||||
|
|
||||||
OAuthClient.AccessTokenResponse response = oauth.doAccessTokenRequest(code, "password");
|
OAuthClient.AccessTokenResponse response = oauth.doAccessTokenRequest(code, "password");
|
||||||
Assert.assertEquals(400, response.getStatusCode());
|
Assert.assertEquals(400, response.getStatusCode());
|
||||||
|
|
|
@ -36,6 +36,7 @@ import org.keycloak.models.Constants;
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.testsuite.AbstractKeycloakTest;
|
import org.keycloak.testsuite.AbstractKeycloakTest;
|
||||||
|
import org.keycloak.testsuite.ActionURIUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
|
@ -71,10 +72,7 @@ public class LoginStatusIframeEndpointTest extends AbstractKeycloakTest {
|
||||||
String s = IOUtils.toString(response.getEntity().getContent());
|
String s = IOUtils.toString(response.getEntity().getContent());
|
||||||
response.close();
|
response.close();
|
||||||
|
|
||||||
Matcher matcher = Pattern.compile("action=\"([^\"]*)\"").matcher(s);
|
String action = ActionURIUtils.getActionURIFromPageSource(s);
|
||||||
matcher.find();
|
|
||||||
|
|
||||||
String action = matcher.group(1);
|
|
||||||
|
|
||||||
HttpPost post = new HttpPost(action);
|
HttpPost post = new HttpPost(action);
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,7 @@ import org.keycloak.testsuite.account.AccountTest;
|
||||||
import org.keycloak.testsuite.admin.ApiUtil;
|
import org.keycloak.testsuite.admin.ApiUtil;
|
||||||
import org.keycloak.testsuite.pages.AccountApplicationsPage;
|
import org.keycloak.testsuite.pages.AccountApplicationsPage;
|
||||||
import org.keycloak.testsuite.pages.AppPage;
|
import org.keycloak.testsuite.pages.AppPage;
|
||||||
|
import org.keycloak.testsuite.pages.ErrorPage;
|
||||||
import org.keycloak.testsuite.pages.OAuthGrantPage;
|
import org.keycloak.testsuite.pages.OAuthGrantPage;
|
||||||
import org.keycloak.testsuite.util.ClientManager;
|
import org.keycloak.testsuite.util.ClientManager;
|
||||||
import org.keycloak.testsuite.util.OAuthClient;
|
import org.keycloak.testsuite.util.OAuthClient;
|
||||||
|
@ -77,6 +78,9 @@ public class OAuthGrantTest extends AbstractKeycloakTest {
|
||||||
@Page
|
@Page
|
||||||
protected AppPage appPage;
|
protected AppPage appPage;
|
||||||
|
|
||||||
|
@Page
|
||||||
|
protected ErrorPage errorPage;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addTestRealms(List<RealmRepresentation> testRealms) {
|
public void addTestRealms(List<RealmRepresentation> testRealms) {
|
||||||
|
|
||||||
|
@ -405,4 +409,23 @@ public class OAuthGrantTest extends AbstractKeycloakTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void oauthGrantExpiredAuthSession() throws Exception {
|
||||||
|
oauth.clientId(THIRD_PARTY_APP);
|
||||||
|
oauth.doLoginGrant("test-user@localhost", "password");
|
||||||
|
|
||||||
|
grantPage.assertCurrent();
|
||||||
|
|
||||||
|
// Expire cookies
|
||||||
|
driver.manage().deleteAllCookies();
|
||||||
|
|
||||||
|
grantPage.accept();
|
||||||
|
|
||||||
|
// Assert link "back to application" present
|
||||||
|
errorPage.assertCurrent();
|
||||||
|
String backToAppLink = errorPage.getBackToApplicationLink();
|
||||||
|
ClientRepresentation thirdParty = findClientByClientId(adminClient.realm(REALM_NAME), THIRD_PARTY_APP).toRepresentation();
|
||||||
|
Assert.assertEquals(backToAppLink, thirdParty.getBaseUrl());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -162,6 +162,7 @@
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"consentRequired": true,
|
"consentRequired": true,
|
||||||
|
|
||||||
|
"baseUrl": "http://localhost:8180/auth/realms/master/app/auth",
|
||||||
"redirectUris": [
|
"redirectUris": [
|
||||||
"http://localhost:8180/auth/realms/master/app/*"
|
"http://localhost:8180/auth/realms/master/app/*"
|
||||||
],
|
],
|
||||||
|
|
|
@ -82,8 +82,13 @@ log4j.logger.org.apache.directory.server.ldap.LdapProtocolHandler=error
|
||||||
#log4j.logger.org.apache.http.impl.conn=debug
|
#log4j.logger.org.apache.http.impl.conn=debug
|
||||||
|
|
||||||
# Enable to view details from identity provider authenticator
|
# Enable to view details from identity provider authenticator
|
||||||
log4j.logger.org.keycloak.authentication.authenticators.browser.IdentityProviderAuthenticator=trace
|
#log4j.logger.org.keycloak.authentication.authenticators.browser.IdentityProviderAuthenticator=trace
|
||||||
log4j.logger.org.keycloak.services.resources.IdentityBrokerService=trace
|
#log4j.logger.org.keycloak.services.resources.IdentityBrokerService=trace
|
||||||
log4j.logger.org.keycloak.broker=trace
|
#log4j.logger.org.keycloak.broker=trace
|
||||||
|
|
||||||
#log4j.logger.io.undertow=trace
|
#log4j.logger.io.undertow=trace
|
||||||
|
|
||||||
|
#log4j.logger.org.keycloak.protocol=debug
|
||||||
|
#log4j.logger.org.keycloak.services.resources.LoginActionsService=debug
|
||||||
|
#log4j.logger.org.keycloak.services.managers=debug
|
||||||
|
#log4j.logger.org.keycloak.services.resources.SessionCodeChecks=debug
|
Loading…
Reference in a new issue