verify clientsession actions
This commit is contained in:
parent
2f2a2dced6
commit
9638c0dd83
21 changed files with 248 additions and 682 deletions
|
@ -1,7 +1,6 @@
|
|||
package org.keycloak.protocol.saml;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
|
||||
import org.jboss.resteasy.spi.HttpRequest;
|
||||
import org.jboss.resteasy.spi.HttpResponse;
|
||||
import org.keycloak.ClientConnection;
|
||||
|
@ -17,7 +16,6 @@ import org.keycloak.events.Details;
|
|||
import org.keycloak.events.Errors;
|
||||
import org.keycloak.events.EventBuilder;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.login.LoginFormsProvider;
|
||||
import org.keycloak.models.AuthenticationFlowModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
|
@ -30,14 +28,11 @@ import org.keycloak.models.utils.KeycloakModelUtils;
|
|||
import org.keycloak.protocol.oidc.utils.RedirectUtils;
|
||||
import org.keycloak.saml.common.constants.GeneralConstants;
|
||||
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
|
||||
import org.keycloak.saml.common.exceptions.ConfigurationException;
|
||||
import org.keycloak.saml.common.exceptions.ProcessingException;
|
||||
import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
|
||||
import org.keycloak.services.ErrorPage;
|
||||
import org.keycloak.services.Urls;
|
||||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
import org.keycloak.services.managers.ClientSessionCode;
|
||||
import org.keycloak.services.managers.HttpAuthenticationManager;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.services.resources.RealmsResource;
|
||||
import org.keycloak.util.StreamUtil;
|
||||
|
@ -52,13 +47,10 @@ import javax.ws.rs.QueryParam;
|
|||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.SecurityContext;
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import javax.ws.rs.ext.Providers;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.security.PublicKey;
|
||||
|
@ -291,36 +283,6 @@ public class SamlService {
|
|||
return newBrowserAuthentication(clientSession);
|
||||
}
|
||||
|
||||
private Response oldBrowserAuthentication(ClientSessionModel clientSession) {
|
||||
Response response = authManager.checkNonFormAuthentication(session, clientSession, realm, uriInfo, request, clientConnection, headers, event);
|
||||
if (response != null) return response;
|
||||
|
||||
// SPNEGO/Kerberos authentication TODO: This should be somehow pluggable instead of hardcoded this way (Authentication interceptors?)
|
||||
HttpAuthenticationManager httpAuthManager = new HttpAuthenticationManager(session, clientSession, realm, uriInfo, request, clientConnection, event);
|
||||
HttpAuthenticationManager.HttpAuthOutput httpAuthOutput = httpAuthManager.spnegoAuthenticate(headers);
|
||||
if (httpAuthOutput.getResponse() != null) return httpAuthOutput.getResponse();
|
||||
|
||||
LoginFormsProvider forms = session.getProvider(LoginFormsProvider.class)
|
||||
.setClientSessionCode(new ClientSessionCode(realm, clientSession).getCode());
|
||||
|
||||
// Attach state from SPNEGO authentication
|
||||
if (httpAuthOutput.getChallenge() != null) {
|
||||
httpAuthOutput.getChallenge().sendChallenge(forms);
|
||||
}
|
||||
|
||||
String rememberMeUsername = AuthenticationManager.getRememberMeUsername(realm, headers);
|
||||
|
||||
if (rememberMeUsername != null) {
|
||||
MultivaluedMap<String, String> formData = new MultivaluedMapImpl<String, String>();
|
||||
formData.add(AuthenticationManager.FORM_USERNAME, rememberMeUsername);
|
||||
formData.add("rememberMe", "on");
|
||||
|
||||
forms.setFormData(formData);
|
||||
}
|
||||
|
||||
return forms.createLogin();
|
||||
}
|
||||
|
||||
private Response buildRedirectToIdentityProvider(String providerId, String accessCode) {
|
||||
logger.debug("Automatically redirect to identity provider: " + providerId);
|
||||
return Response.temporaryRedirect(
|
||||
|
|
|
@ -43,6 +43,12 @@ public class AuthenticationProcessor {
|
|||
protected HttpRequest request;
|
||||
protected String flowId;
|
||||
protected String action;
|
||||
/**
|
||||
* This could be an error message forwarded from brokering when the broker failed authentication
|
||||
* and we want to continue authentication locally. forwardedErrorMessage can then be displayed by
|
||||
* whatever form is challenging.
|
||||
*/
|
||||
protected String forwardedErrorMessage;
|
||||
protected boolean userSessionCreated;
|
||||
|
||||
|
||||
|
@ -56,6 +62,7 @@ public class AuthenticationProcessor {
|
|||
|
||||
}
|
||||
public static enum Error {
|
||||
INVALID_CLIENT_SESSION,
|
||||
INVALID_USER,
|
||||
INVALID_CREDENTIALS,
|
||||
CREDENTIAL_SETUP_REQUIRED,
|
||||
|
@ -144,6 +151,11 @@ public class AuthenticationProcessor {
|
|||
return this;
|
||||
}
|
||||
|
||||
public AuthenticationProcessor setForwardedErrorMessage(String forwardedErrorMessage) {
|
||||
this.forwardedErrorMessage = forwardedErrorMessage;
|
||||
return this;
|
||||
}
|
||||
|
||||
private class Result implements AuthenticatorContext {
|
||||
AuthenticatorModel model;
|
||||
AuthenticationExecutionModel execution;
|
||||
|
@ -300,6 +312,11 @@ public class AuthenticationProcessor {
|
|||
public EventBuilder getEvent() {
|
||||
return AuthenticationProcessor.this.event;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getForwardedErrorMessage() {
|
||||
return AuthenticationProcessor.this.forwardedErrorMessage;
|
||||
}
|
||||
}
|
||||
|
||||
public static class AuthException extends RuntimeException {
|
||||
|
@ -367,7 +384,11 @@ public class AuthenticationProcessor {
|
|||
event.error(Errors.USER_TEMPORARILY_DISABLED);
|
||||
return ErrorPage.error(session, Messages.ACCOUNT_TEMPORARILY_DISABLED);
|
||||
|
||||
} else {
|
||||
} else if (e.getError() == Error.INVALID_CLIENT_SESSION) {
|
||||
event.error(Errors.INVALID_CODE);
|
||||
return ErrorPage.error(session, Messages.INVALID_CODE);
|
||||
|
||||
}else {
|
||||
event.error(Errors.INVALID_USER_CREDENTIALS);
|
||||
return ErrorPage.error(session, Messages.INVALID_USER);
|
||||
}
|
||||
|
@ -382,6 +403,9 @@ public class AuthenticationProcessor {
|
|||
|
||||
|
||||
public Response authenticate() throws AuthException {
|
||||
if (!ClientSessionModel.Action.AUTHENTICATE.name().equals(clientSession.getAction())) {
|
||||
throw new AuthException(Error.INVALID_CLIENT_SESSION);
|
||||
}
|
||||
logger.debug("AUTHENTICATE");
|
||||
event.event(EventType.LOGIN);
|
||||
event.client(clientSession.getClient().getClientId())
|
||||
|
@ -402,6 +426,9 @@ public class AuthenticationProcessor {
|
|||
}
|
||||
|
||||
public Response authenticateOnly() throws AuthException {
|
||||
if (!ClientSessionModel.Action.AUTHENTICATE.name().equals(clientSession.getAction())) {
|
||||
throw new AuthException(Error.INVALID_CLIENT_SESSION);
|
||||
}
|
||||
event.event(EventType.LOGIN);
|
||||
event.client(clientSession.getClient().getClientId())
|
||||
.detail(Details.REDIRECT_URI, clientSession.getRedirectUri())
|
||||
|
@ -585,11 +612,6 @@ public class AuthenticationProcessor {
|
|||
.detail(Details.USERNAME, username)
|
||||
.session(userSession);
|
||||
|
||||
return processRequiredActions();
|
||||
|
||||
}
|
||||
|
||||
public Response processRequiredActions() {
|
||||
return AuthenticationManager.nextActionAfterAuthentication(session, userSession, clientSession, connection, request, uriInfo, event);
|
||||
|
||||
}
|
||||
|
|
|
@ -66,4 +66,11 @@ public interface AuthenticatorContext {
|
|||
|
||||
void failureChallenge(AuthenticationProcessor.Error error, Response challenge);
|
||||
void attempted();
|
||||
|
||||
/**
|
||||
* This could be an error message forwarded from brokering when the broker failed authentication
|
||||
* and we want to continue authentication locally. forwardedErrorMessage can then be displayed by
|
||||
* whatever form is challenging.
|
||||
*/
|
||||
String getForwardedErrorMessage();
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.services.managers.BruteForceProtector;
|
||||
import org.keycloak.services.managers.ClientSessionCode;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
@ -29,4 +30,5 @@ public interface RequiredActionContext {
|
|||
UriInfo getUriInfo();
|
||||
KeycloakSession getSession();
|
||||
HttpRequest getHttpRequest();
|
||||
String generateAccessCode(String action);
|
||||
}
|
||||
|
|
|
@ -12,4 +12,5 @@ public interface RequiredActionProvider extends Provider {
|
|||
void evaluateTriggers(RequiredActionContext context);
|
||||
Response invokeRequiredAction(RequiredActionContext context);
|
||||
Object jaxrsService(RequiredActionContext context);
|
||||
String getProviderId();
|
||||
}
|
||||
|
|
|
@ -1,37 +1,25 @@
|
|||
package org.keycloak.authentication.actions;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.Version;
|
||||
import org.keycloak.authentication.RequiredActionContext;
|
||||
import org.keycloak.authentication.RequiredActionFactory;
|
||||
import org.keycloak.authentication.RequiredActionProvider;
|
||||
import org.keycloak.events.Errors;
|
||||
import org.keycloak.freemarker.BrowserSecurityHeaderSetup;
|
||||
import org.keycloak.freemarker.FreeMarkerException;
|
||||
import org.keycloak.freemarker.FreeMarkerUtil;
|
||||
import org.keycloak.freemarker.Theme;
|
||||
import org.keycloak.freemarker.ThemeProvider;
|
||||
import org.keycloak.login.LoginFormsProvider;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.protocol.LoginProtocol;
|
||||
import org.keycloak.services.Urls;
|
||||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
import org.keycloak.services.managers.ClientSessionCode;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -86,6 +74,14 @@ public class TermsAndConditions implements RequiredActionProvider, RequiredActio
|
|||
return PROVIDER_ID;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getProviderId() {
|
||||
return getId();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void evaluateTriggers(RequiredActionContext context) {
|
||||
|
||||
|
@ -94,7 +90,7 @@ public class TermsAndConditions implements RequiredActionProvider, RequiredActio
|
|||
@Override
|
||||
public Response invokeRequiredAction(RequiredActionContext context) {
|
||||
return context.getSession().getProvider(LoginFormsProvider.class)
|
||||
.setClientSessionCode(new ClientSessionCode(context.getRealm(), context.getClientSession()).getCode())
|
||||
.setClientSessionCode(context.generateAccessCode(getProviderId()))
|
||||
.setUser(context.getUser())
|
||||
.createForm("terms.ftl", new HashMap<String, Object>());
|
||||
}
|
||||
|
|
|
@ -51,10 +51,9 @@ public class UpdatePassword implements RequiredActionProvider, RequiredActionFac
|
|||
|
||||
@Override
|
||||
public Response invokeRequiredAction(RequiredActionContext context) {
|
||||
ClientSessionCode accessCode = new ClientSessionCode(context.getRealm(), context.getClientSession());
|
||||
accessCode.setAction(ClientSessionModel.Action.UPDATE_PASSWORD.name());
|
||||
|
||||
LoginFormsProvider loginFormsProvider = context.getSession().getProvider(LoginFormsProvider.class).setClientSessionCode(accessCode.getCode())
|
||||
LoginFormsProvider loginFormsProvider = context.getSession()
|
||||
.getProvider(LoginFormsProvider.class)
|
||||
.setClientSessionCode(context.generateAccessCode(getProviderId()))
|
||||
.setUser(context.getUser());
|
||||
return loginFormsProvider.createResponse(UserModel.RequiredAction.UPDATE_PASSWORD);
|
||||
}
|
||||
|
@ -96,4 +95,10 @@ public class UpdatePassword implements RequiredActionProvider, RequiredActionFac
|
|||
public String getId() {
|
||||
return UserModel.RequiredAction.UPDATE_PASSWORD.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProviderId() {
|
||||
return getId();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -32,10 +32,8 @@ public class UpdateProfile implements RequiredActionProvider, RequiredActionFact
|
|||
|
||||
@Override
|
||||
public Response invokeRequiredAction(RequiredActionContext context) {
|
||||
ClientSessionCode accessCode = new ClientSessionCode(context.getRealm(), context.getClientSession());
|
||||
accessCode.setAction(ClientSessionModel.Action.UPDATE_PROFILE.name());
|
||||
|
||||
LoginFormsProvider loginFormsProvider = context.getSession().getProvider(LoginFormsProvider.class).setClientSessionCode(accessCode.getCode())
|
||||
LoginFormsProvider loginFormsProvider = context.getSession().getProvider(LoginFormsProvider.class)
|
||||
.setClientSessionCode(context.generateAccessCode(getProviderId()))
|
||||
.setUser(context.getUser());
|
||||
return loginFormsProvider.createResponse(UserModel.RequiredAction.UPDATE_PROFILE);
|
||||
}
|
||||
|
@ -78,4 +76,10 @@ public class UpdateProfile implements RequiredActionProvider, RequiredActionFact
|
|||
public String getId() {
|
||||
return UserModel.RequiredAction.UPDATE_PROFILE.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProviderId() {
|
||||
return getId();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -40,10 +40,8 @@ public class UpdateTotp implements RequiredActionProvider, RequiredActionFactory
|
|||
|
||||
@Override
|
||||
public Response invokeRequiredAction(RequiredActionContext context) {
|
||||
ClientSessionCode accessCode = new ClientSessionCode(context.getRealm(), context.getClientSession());
|
||||
accessCode.setAction(ClientSessionModel.Action.CONFIGURE_TOTP.name());
|
||||
|
||||
LoginFormsProvider loginFormsProvider = context.getSession().getProvider(LoginFormsProvider.class).setClientSessionCode(accessCode.getCode())
|
||||
LoginFormsProvider loginFormsProvider = context.getSession().getProvider(LoginFormsProvider.class)
|
||||
.setClientSessionCode(context.generateAccessCode(getProviderId()))
|
||||
.setUser(context.getUser());
|
||||
return loginFormsProvider.createResponse(UserModel.RequiredAction.CONFIGURE_TOTP);
|
||||
}
|
||||
|
@ -87,4 +85,10 @@ public class UpdateTotp implements RequiredActionProvider, RequiredActionFactory
|
|||
return UserModel.RequiredAction.CONFIGURE_TOTP.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProviderId() {
|
||||
return getId();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -59,12 +59,11 @@ public class VerifyEmail implements RequiredActionProvider, RequiredActionFactor
|
|||
return null;
|
||||
}
|
||||
|
||||
ClientSessionCode accessCode = new ClientSessionCode(context.getRealm(), context.getClientSession());
|
||||
accessCode.setAction(ClientSessionModel.Action.VERIFY_EMAIL.name());
|
||||
context.getEvent().clone().event(EventType.SEND_VERIFY_EMAIL).detail(Details.EMAIL, context.getUser().getEmail()).success();
|
||||
LoginActionsService.createActionCookie(context.getRealm(), context.getUriInfo(), context.getConnection(), context.getUserSession().getId());
|
||||
|
||||
LoginFormsProvider loginFormsProvider = context.getSession().getProvider(LoginFormsProvider.class).setClientSessionCode(accessCode.getCode())
|
||||
LoginFormsProvider loginFormsProvider = context.getSession().getProvider(LoginFormsProvider.class)
|
||||
.setClientSessionCode(context.generateAccessCode(getProviderId()))
|
||||
.setUser(context.getUser());
|
||||
return loginFormsProvider.createResponse(UserModel.RequiredAction.VERIFY_EMAIL);
|
||||
}
|
||||
|
@ -107,4 +106,10 @@ public class VerifyEmail implements RequiredActionProvider, RequiredActionFactor
|
|||
return UserModel.RequiredAction.VERIFY_EMAIL.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProviderId() {
|
||||
return getId();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -32,10 +32,14 @@ public class AbstractFormAuthenticator {
|
|||
ClientSessionCode code = new ClientSessionCode(context.getRealm(), context.getClientSession());
|
||||
code.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
|
||||
URI action = getActionUrl(context, code, LOGIN_FORM_ACTION);
|
||||
return context.getSession().getProvider(LoginFormsProvider.class)
|
||||
LoginFormsProvider provider = context.getSession().getProvider(LoginFormsProvider.class)
|
||||
.setUser(context.getUser())
|
||||
.setActionUri(action)
|
||||
.setClientSessionCode(code.getCode());
|
||||
if (context.getForwardedErrorMessage() != null) {
|
||||
provider.setError(context.getForwardedErrorMessage());
|
||||
}
|
||||
return provider;
|
||||
}
|
||||
|
||||
public static URI getActionUrl(AuthenticatorContext context, ClientSessionCode code, String action) {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package org.keycloak.protocol.oidc.endpoints;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
|
||||
import org.jboss.resteasy.spi.HttpRequest;
|
||||
import org.keycloak.ClientConnection;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
|
@ -18,7 +17,6 @@ import org.keycloak.models.ClientSessionModel;
|
|||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RequiredCredentialModel;
|
||||
import org.keycloak.models.utils.DefaultAuthenticationFlows;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
|
@ -26,7 +24,6 @@ import org.keycloak.protocol.oidc.utils.RedirectUtils;
|
|||
import org.keycloak.services.ErrorPageException;
|
||||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
import org.keycloak.services.managers.ClientSessionCode;
|
||||
import org.keycloak.services.managers.HttpAuthenticationManager;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.services.Urls;
|
||||
|
||||
|
@ -248,10 +245,10 @@ public class AuthorizationEndpoint {
|
|||
return buildRedirectToIdentityProvider(idpHint, accessCode);
|
||||
}
|
||||
|
||||
return newBrowserAuthentication(accessCode);
|
||||
return browserAuthentication(accessCode);
|
||||
}
|
||||
|
||||
protected Response newBrowserAuthentication(String accessCode) {
|
||||
protected Response browserAuthentication(String accessCode) {
|
||||
List<IdentityProviderModel> identityProviders = realm.getIdentityProviders();
|
||||
for (IdentityProviderModel identityProvider : identityProviders) {
|
||||
if (identityProvider.isAuthenticateByDefault()) {
|
||||
|
@ -295,65 +292,6 @@ public class AuthorizationEndpoint {
|
|||
}
|
||||
}
|
||||
|
||||
protected Response oldBrowserAuthentication(String accessCode) {
|
||||
Response response = authManager.checkNonFormAuthentication(session, clientSession, realm, uriInfo, request, clientConnection, headers, event);
|
||||
if (response != null) return response;
|
||||
|
||||
// SPNEGO/Kerberos authentication TODO: This should be somehow pluggable instead of hardcoded this way (Authentication interceptors?)
|
||||
HttpAuthenticationManager httpAuthManager = new HttpAuthenticationManager(session, clientSession, realm, uriInfo, request, clientConnection, event);
|
||||
HttpAuthenticationManager.HttpAuthOutput httpAuthOutput = httpAuthManager.spnegoAuthenticate(headers);
|
||||
if (httpAuthOutput.getResponse() != null) return httpAuthOutput.getResponse();
|
||||
|
||||
if (prompt != null && prompt.equals("none")) {
|
||||
OIDCLoginProtocol oauth = new OIDCLoginProtocol(session, realm, uriInfo, headers, event);
|
||||
return oauth.cancelLogin(clientSession);
|
||||
}
|
||||
|
||||
List<IdentityProviderModel> identityProviders = realm.getIdentityProviders();
|
||||
for (IdentityProviderModel identityProvider : identityProviders) {
|
||||
if (identityProvider.isAuthenticateByDefault()) {
|
||||
return buildRedirectToIdentityProvider(identityProvider.getAlias(), accessCode);
|
||||
}
|
||||
}
|
||||
|
||||
List<RequiredCredentialModel> requiredCredentials = realm.getRequiredCredentials();
|
||||
if (requiredCredentials.isEmpty()) {
|
||||
if (!identityProviders.isEmpty()) {
|
||||
if (identityProviders.size() == 1) {
|
||||
return buildRedirectToIdentityProvider(identityProviders.get(0).getAlias(), accessCode);
|
||||
}
|
||||
|
||||
return session.getProvider(LoginFormsProvider.class).setError(Messages.IDENTITY_PROVIDER_NOT_UNIQUE, realm.getName()).createErrorPage();
|
||||
}
|
||||
|
||||
return session.getProvider(LoginFormsProvider.class).setError(Messages.REALM_SUPPORTS_NO_CREDENTIALS, realm.getName()).createErrorPage();
|
||||
}
|
||||
|
||||
LoginFormsProvider forms = session.getProvider(LoginFormsProvider.class).setClientSessionCode(accessCode);
|
||||
|
||||
// Attach state from SPNEGO authentication
|
||||
if (httpAuthOutput.getChallenge() != null) {
|
||||
httpAuthOutput.getChallenge().sendChallenge(forms);
|
||||
}
|
||||
|
||||
String rememberMeUsername = AuthenticationManager.getRememberMeUsername(realm, headers);
|
||||
|
||||
if (loginHint != null || rememberMeUsername != null) {
|
||||
MultivaluedMap<String, String> formData = new MultivaluedMapImpl<String, String>();
|
||||
|
||||
if (loginHint != null) {
|
||||
formData.add(AuthenticationManager.FORM_USERNAME, loginHint);
|
||||
} else {
|
||||
formData.add(AuthenticationManager.FORM_USERNAME, rememberMeUsername);
|
||||
formData.add("rememberMe", "on");
|
||||
}
|
||||
|
||||
forms.setFormData(formData);
|
||||
}
|
||||
|
||||
return forms.createLogin();
|
||||
}
|
||||
|
||||
private Response buildRegister() {
|
||||
authManager.expireIdentityCookie(realm, uriInfo, clientConnection);
|
||||
|
||||
|
|
|
@ -379,22 +379,6 @@ public class AuthenticationManager {
|
|||
return authResult;
|
||||
}
|
||||
|
||||
public Response checkNonFormAuthentication(KeycloakSession session, ClientSessionModel clientSession, RealmModel realm, UriInfo uriInfo,
|
||||
HttpRequest request,
|
||||
ClientConnection clientConnection, HttpHeaders headers,
|
||||
EventBuilder event) {
|
||||
AuthResult authResult = authenticateIdentityCookie(session, realm, uriInfo, clientConnection, headers, true);
|
||||
if (authResult != null) {
|
||||
UserModel user = authResult.getUser();
|
||||
UserSessionModel userSession = authResult.getSession();
|
||||
TokenManager.attachClientSession(userSession, clientSession);
|
||||
event.user(user).session(userSession).detail(Details.AUTH_METHOD, "sso");
|
||||
return nextActionAfterAuthentication(session, userSession, clientSession, clientConnection, request, uriInfo, event);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static Response redirectAfterSuccessfulFlow(KeycloakSession session, RealmModel realm, UserSessionModel userSession,
|
||||
ClientSessionModel clientSession,
|
||||
|
@ -442,11 +426,6 @@ public class AuthenticationManager {
|
|||
final HttpRequest request, final UriInfo uriInfo, final EventBuilder event) {
|
||||
final RealmModel realm = clientSession.getRealm();
|
||||
final UserModel user = userSession.getUser();
|
||||
/*
|
||||
isForcePasswordUpdateRequired(realm, user);
|
||||
isTotpConfigurationRequired(realm, user);
|
||||
isEmailVerificationRequired(realm, user);
|
||||
*/
|
||||
final ClientModel client = clientSession.getClient();
|
||||
|
||||
RequiredActionContext context = new RequiredActionContext() {
|
||||
|
@ -494,6 +473,13 @@ public class AuthenticationManager {
|
|||
public HttpRequest getHttpRequest() {
|
||||
return request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateAccessCode(String action) {
|
||||
ClientSessionCode code = new ClientSessionCode(getRealm(), getClientSession());
|
||||
code.setAction(action);
|
||||
return code.getCode();
|
||||
}
|
||||
};
|
||||
|
||||
// see if any required actions need triggering, i.e. an expired password
|
||||
|
@ -502,7 +488,6 @@ public class AuthenticationManager {
|
|||
provider.evaluateTriggers(context);
|
||||
}
|
||||
|
||||
ClientSessionCode accessCode = new ClientSessionCode(realm, clientSession);
|
||||
|
||||
logger.debugv("processAccessCode: go to oauth page?: {0}", client.isConsentRequired());
|
||||
|
||||
|
@ -512,7 +497,9 @@ public class AuthenticationManager {
|
|||
for (String action : requiredActions) {
|
||||
RequiredActionProvider actionProvider = session.getProvider(RequiredActionProvider.class, action);
|
||||
Response challenge = actionProvider.invokeRequiredAction(context);
|
||||
if (challenge != null) return challenge;
|
||||
if (challenge != null) {
|
||||
return challenge;
|
||||
}
|
||||
|
||||
}
|
||||
if (client.isConsentRequired()) {
|
||||
|
@ -521,6 +508,7 @@ public class AuthenticationManager {
|
|||
|
||||
List<RoleModel> realmRoles = new LinkedList<>();
|
||||
MultivaluedMap<String, RoleModel> resourceRoles = new MultivaluedMapImpl<>();
|
||||
ClientSessionCode accessCode = new ClientSessionCode(realm, clientSession);
|
||||
for (RoleModel r : accessCode.getRequestedRoles()) {
|
||||
|
||||
// Consent already granted by user
|
||||
|
@ -564,47 +552,6 @@ public class AuthenticationManager {
|
|||
}
|
||||
|
||||
|
||||
|
||||
private static void isForcePasswordUpdateRequired(RealmModel realm, UserModel user) {
|
||||
int daysToExpirePassword = realm.getPasswordPolicy().getDaysToExpirePassword();
|
||||
if(daysToExpirePassword != -1) {
|
||||
for (UserCredentialValueModel entity : user.getCredentialsDirectly()) {
|
||||
if (entity.getType().equals(UserCredentialModel.PASSWORD)) {
|
||||
|
||||
if(entity.getCreatedDate() == null) {
|
||||
user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
|
||||
logger.debug("User is required to update password");
|
||||
} else {
|
||||
long timeElapsed = Time.toMillis(Time.currentTime()) - entity.getCreatedDate();
|
||||
long timeToExpire = TimeUnit.DAYS.toMillis(daysToExpirePassword);
|
||||
|
||||
if(timeElapsed > timeToExpire) {
|
||||
user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
|
||||
logger.debug("User is required to update password");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static void isTotpConfigurationRequired(RealmModel realm, UserModel user) {
|
||||
for (RequiredCredentialModel c : realm.getRequiredCredentials()) {
|
||||
if (c.getType().equals(CredentialRepresentation.TOTP) && !user.isTotp()) {
|
||||
user.addRequiredAction(UserModel.RequiredAction.CONFIGURE_TOTP);
|
||||
logger.debug("User is required to configure totp");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static void isEmailVerificationRequired(RealmModel realm, UserModel user) {
|
||||
if (realm.isVerifyEmail() && !user.isEmailVerified()) {
|
||||
user.addRequiredAction(UserModel.RequiredAction.VERIFY_EMAIL);
|
||||
logger.debug("User is required to verify email");
|
||||
}
|
||||
}
|
||||
|
||||
protected static AuthResult verifyIdentityToken(KeycloakSession session, RealmModel realm, UriInfo uriInfo, ClientConnection connection, boolean checkActive, String tokenString, HttpHeaders headers) {
|
||||
try {
|
||||
AccessToken token = RSATokenVerifier.verifyToken(tokenString, realm.getPublicKey(), Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName()), checkActive);
|
||||
|
|
|
@ -81,22 +81,18 @@ public class ClientSessionCode {
|
|||
}
|
||||
|
||||
public boolean isValid(String requestedAction) {
|
||||
String action = clientSession.getAction();
|
||||
if (action == null) {
|
||||
return false;
|
||||
}
|
||||
if (!isValidAction(requestedAction)) return false;
|
||||
return isActionActive(requestedAction);
|
||||
}
|
||||
|
||||
public boolean isActionActive(String requestedAction) {
|
||||
int timestamp = clientSession.getTimestamp();
|
||||
|
||||
if (!action.equals(requestedAction)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int lifespan;
|
||||
if (action.equals(ClientSessionModel.Action.CODE_TO_TOKEN.name())) {
|
||||
if (requestedAction.equals(ClientSessionModel.Action.CODE_TO_TOKEN.name())) {
|
||||
lifespan = realm.getAccessCodeLifespan();
|
||||
|
||||
} else if (action.equals(ClientSessionModel.Action.AUTHENTICATE.name())) {
|
||||
} else if (requestedAction.equals(ClientSessionModel.Action.AUTHENTICATE.name())) {
|
||||
lifespan = realm.getAccessCodeLifespanLogin() > 0 ? realm.getAccessCodeLifespanLogin() : realm.getAccessCodeLifespanUserAction();
|
||||
} else {
|
||||
lifespan = realm.getAccessCodeLifespanUserAction();
|
||||
|
@ -104,6 +100,18 @@ public class ClientSessionCode {
|
|||
return timestamp + lifespan > Time.currentTime();
|
||||
}
|
||||
|
||||
public boolean isValidAction(String requestedAction) {
|
||||
String action = clientSession.getAction();
|
||||
if (action == null) {
|
||||
return false;
|
||||
}
|
||||
if (!action.equals(requestedAction)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public Set<RoleModel> getRequestedRoles() {
|
||||
Set<RoleModel> requestedRoles = new HashSet<RoleModel>();
|
||||
for (String roleId : clientSession.getRoles()) {
|
||||
|
|
|
@ -1,186 +0,0 @@
|
|||
package org.keycloak.services.managers;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.jboss.resteasy.spi.HttpRequest;
|
||||
import org.keycloak.ClientConnection;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.Errors;
|
||||
import org.keycloak.events.EventBuilder;
|
||||
import org.keycloak.login.LoginFormsProvider;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.CredentialValidationOutput;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RequiredCredentialModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.constants.KerberosConstants;
|
||||
import org.keycloak.protocol.oidc.TokenManager;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.services.ErrorPage;
|
||||
|
||||
/**
|
||||
* Handle HTTP authentication types requiring complex handshakes with multiple HTTP request/responses
|
||||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class HttpAuthenticationManager {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(HttpAuthenticationManager.class);
|
||||
|
||||
private KeycloakSession session;
|
||||
private RealmModel realm;
|
||||
private UriInfo uriInfo;
|
||||
private HttpRequest request;
|
||||
private EventBuilder event;
|
||||
private ClientConnection clientConnection;
|
||||
private ClientSessionModel clientSession;
|
||||
|
||||
public HttpAuthenticationManager(KeycloakSession session, ClientSessionModel clientSession, RealmModel realm, UriInfo uriInfo,
|
||||
HttpRequest request,
|
||||
ClientConnection clientConnection,
|
||||
EventBuilder event) {
|
||||
this.session = session;
|
||||
this.realm = realm;
|
||||
this.uriInfo = uriInfo;
|
||||
this.request = request;
|
||||
this.event = event;
|
||||
this.clientConnection = clientConnection;
|
||||
this.clientSession = clientSession;
|
||||
}
|
||||
|
||||
|
||||
public HttpAuthOutput spnegoAuthenticate(HttpHeaders headers) {
|
||||
boolean kerberosSupported = false;
|
||||
for (RequiredCredentialModel c : realm.getRequiredCredentials()) {
|
||||
if (c.getType().equals(CredentialRepresentation.KERBEROS)) {
|
||||
kerberosSupported = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
String log = kerberosSupported ? "SPNEGO authentication is supported" : "SPNEGO authentication is not supported";
|
||||
logger.trace(log);
|
||||
}
|
||||
|
||||
if (!kerberosSupported) {
|
||||
return new HttpAuthOutput(null, null);
|
||||
}
|
||||
|
||||
String authHeader = request.getHttpHeaders().getRequestHeaders().getFirst(HttpHeaders.AUTHORIZATION);
|
||||
|
||||
// Case when we don't yet have any Negotiate header
|
||||
if (authHeader == null) {
|
||||
return challengeNegotiation(null);
|
||||
}
|
||||
|
||||
String[] tokens = authHeader.split(" ");
|
||||
if (tokens.length != 2) {
|
||||
logger.warn("Invalid length of tokens: " + tokens.length);
|
||||
return challengeNegotiation(null);
|
||||
} else if (!KerberosConstants.NEGOTIATE.equalsIgnoreCase(tokens[0])) {
|
||||
logger.warn("Unknown scheme " + tokens[0]);
|
||||
return challengeNegotiation(null);
|
||||
} else {
|
||||
String spnegoToken = tokens[1];
|
||||
UserCredentialModel spnegoCredential = UserCredentialModel.kerberos(spnegoToken);
|
||||
|
||||
CredentialValidationOutput output = session.users().validCredentials(realm, spnegoCredential);
|
||||
|
||||
if (output.getAuthStatus() == CredentialValidationOutput.Status.AUTHENTICATED) {
|
||||
return sendResponse(output.getAuthenticatedUser(), output.getState(), "spnego", headers);
|
||||
} else {
|
||||
String spnegoResponseToken = (String) output.getState().get(KerberosConstants.RESPONSE_TOKEN);
|
||||
return challengeNegotiation(spnegoResponseToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Send response after successful authentication
|
||||
private HttpAuthOutput sendResponse(UserModel user, Map<String, String> authState, String authMethod, HttpHeaders headers) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("User " + user.getUsername() + " authenticated with " + authMethod);
|
||||
}
|
||||
|
||||
Response response;
|
||||
if (!user.isEnabled()) {
|
||||
event.error(Errors.USER_DISABLED);
|
||||
response = ErrorPage.error(session, Messages.ACCOUNT_DISABLED);
|
||||
} else {
|
||||
UserSessionModel userSession = session.sessions().createUserSession(realm, user, user.getUsername(), clientConnection.getRemoteAddr(), authMethod, false, null, null);
|
||||
|
||||
// Propagate state (like kerberos delegation credentials etc) as attributes of userSession
|
||||
for (Map.Entry<String, String> entry : authState.entrySet()) {
|
||||
userSession.setNote(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
TokenManager.attachClientSession(userSession, clientSession);
|
||||
event.user(user)
|
||||
.session(userSession)
|
||||
.detail(Details.AUTH_METHOD, authMethod)
|
||||
.detail(Details.USERNAME, user.getUsername());
|
||||
response = AuthenticationManager.nextActionAfterAuthentication(session, userSession, clientSession, clientConnection, request, uriInfo, event);
|
||||
}
|
||||
|
||||
return new HttpAuthOutput(response, null);
|
||||
}
|
||||
|
||||
|
||||
private HttpAuthOutput challengeNegotiation(final String negotiateToken) {
|
||||
return new HttpAuthOutput(null, new HttpAuthChallenge() {
|
||||
|
||||
@Override
|
||||
public void sendChallenge(LoginFormsProvider loginFormsProvider) {
|
||||
String negotiateHeader = negotiateToken == null ? KerberosConstants.NEGOTIATE : KerberosConstants.NEGOTIATE + " " + negotiateToken;
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Sending back " + HttpHeaders.WWW_AUTHENTICATE + ": " + negotiateHeader);
|
||||
}
|
||||
|
||||
loginFormsProvider.setStatus(Response.Status.UNAUTHORIZED);
|
||||
loginFormsProvider.setResponseHeader(HttpHeaders.WWW_AUTHENTICATE, negotiateHeader);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public class HttpAuthOutput {
|
||||
|
||||
// It's non-null if we want to immediately send response to user
|
||||
private final Response response;
|
||||
|
||||
// It's non-null if challenge should be attached to rendered login form
|
||||
private final HttpAuthChallenge challenge;
|
||||
|
||||
public HttpAuthOutput(Response response, HttpAuthChallenge challenge) {
|
||||
this.response = response;
|
||||
this.challenge = challenge;
|
||||
}
|
||||
|
||||
public Response getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
public HttpAuthChallenge getChallenge() {
|
||||
return challenge;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public interface HttpAuthChallenge {
|
||||
|
||||
void sendChallenge(LoginFormsProvider loginFormsProvider);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -21,6 +21,7 @@ import org.jboss.logging.Logger;
|
|||
import org.jboss.resteasy.spi.HttpRequest;
|
||||
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||
import org.keycloak.ClientConnection;
|
||||
import org.keycloak.authentication.AuthenticationProcessor;
|
||||
import org.keycloak.broker.provider.AuthenticationRequest;
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.broker.provider.IdentityBrokerException;
|
||||
|
@ -31,7 +32,7 @@ import org.keycloak.events.Details;
|
|||
import org.keycloak.events.Errors;
|
||||
import org.keycloak.events.EventBuilder;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.login.LoginFormsProvider;
|
||||
import org.keycloak.models.AuthenticationFlowModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.Constants;
|
||||
|
@ -44,6 +45,7 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.models.utils.DefaultAuthenticationFlows;
|
||||
import org.keycloak.protocol.oidc.TokenManager;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
|
@ -51,6 +53,7 @@ import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
|||
import org.keycloak.services.managers.AppAuthManager;
|
||||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
import org.keycloak.services.managers.AuthenticationManager.AuthResult;
|
||||
import org.keycloak.services.managers.BruteForceProtector;
|
||||
import org.keycloak.services.managers.ClientSessionCode;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.services.ErrorResponse;
|
||||
|
@ -112,11 +115,13 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
|||
|
||||
private EventBuilder event;
|
||||
|
||||
public IdentityBrokerService(RealmModel realmModel) {
|
||||
private BruteForceProtector protector;
|
||||
|
||||
public IdentityBrokerService(RealmModel realmModel, BruteForceProtector protector) {
|
||||
if (realmModel == null) {
|
||||
throw new IllegalArgumentException("Realm can not be null.");
|
||||
}
|
||||
|
||||
this.protector = protector;
|
||||
this.realmModel = realmModel;
|
||||
}
|
||||
|
||||
|
@ -317,12 +322,21 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
|||
|
||||
@Override
|
||||
public Response cancelled(String code) {
|
||||
return session.getProvider(LoginFormsProvider.class).setClientSessionCode(code).createLogin();
|
||||
ClientSessionCode clientCode = ClientSessionCode.parse(code, this.session, this.realmModel);
|
||||
if (clientCode.getClientSession() == null || !clientCode.isValid(AUTHENTICATE.name())) {
|
||||
return redirectToErrorPage(Messages.INVALID_CODE);
|
||||
}
|
||||
|
||||
return browserAuthentication(clientCode.getClientSession(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response error(String code, String message) {
|
||||
return session.getProvider(LoginFormsProvider.class).setClientSessionCode(code).setError(message).createLogin();
|
||||
ClientSessionCode clientCode = ClientSessionCode.parse(code, this.session, this.realmModel);
|
||||
if (clientCode.getClientSession() == null || !clientCode.isValid(AUTHENTICATE.name())) {
|
||||
return redirectToErrorPage(Messages.INVALID_CODE);
|
||||
}
|
||||
return browserAuthentication(clientCode.getClientSession(), message);
|
||||
}
|
||||
|
||||
private Response performAccountLinking(ClientSessionModel clientSession, BrokeredIdentityContext context, FederatedIdentityModel federatedIdentityModel, UserModel federatedUser) {
|
||||
|
@ -448,12 +462,32 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
|||
}
|
||||
|
||||
fireErrorEvent(message);
|
||||
return session.getProvider(LoginFormsProvider.class)
|
||||
.setClientSessionCode(clientCode.getCode())
|
||||
.setError(message)
|
||||
.createLogin();
|
||||
return browserAuthentication(clientCode.getClientSession(), message);
|
||||
}
|
||||
|
||||
protected Response browserAuthentication(ClientSessionModel clientSession, String errorMessage) {
|
||||
AuthenticationFlowModel flow = realmModel.getFlowByAlias(DefaultAuthenticationFlows.BROWSER_FLOW);
|
||||
String flowId = flow.getId();
|
||||
AuthenticationProcessor processor = new AuthenticationProcessor();
|
||||
processor.setClientSession(clientSession)
|
||||
.setFlowId(flowId)
|
||||
.setConnection(clientConnection)
|
||||
.setEventBuilder(event)
|
||||
.setProtector(protector)
|
||||
.setRealm(realmModel)
|
||||
.setSession(session)
|
||||
.setUriInfo(uriInfo)
|
||||
.setRequest(request);
|
||||
if (errorMessage != null) processor.setForwardedErrorMessage(errorMessage);
|
||||
|
||||
try {
|
||||
return processor.authenticate();
|
||||
} catch (Exception e) {
|
||||
return processor.handleBrowserException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Response badRequest(String message) {
|
||||
fireErrorEvent(message);
|
||||
return ErrorResponse.error(message, Status.BAD_REQUEST);
|
||||
|
|
|
@ -36,9 +36,7 @@ import org.keycloak.events.Details;
|
|||
import org.keycloak.events.Errors;
|
||||
import org.keycloak.events.EventBuilder;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.jose.jws.JWSBuilder;
|
||||
import org.keycloak.login.LoginFormsProvider;
|
||||
import org.keycloak.models.AuthenticationExecutionModel;
|
||||
import org.keycloak.models.AuthenticationFlowModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
|
@ -48,20 +46,17 @@ import org.keycloak.models.KeycloakSession;
|
|||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RequiredCredentialModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserModel.RequiredAction;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.models.utils.DefaultAuthenticationFlows;
|
||||
import org.keycloak.models.utils.FormMessage;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.models.utils.TimeBasedOTP;
|
||||
import org.keycloak.protocol.LoginProtocol;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
|
||||
import org.keycloak.protocol.oidc.TokenManager;
|
||||
import org.keycloak.representations.PasswordToken;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
import org.keycloak.services.managers.ClientSessionCode;
|
||||
|
@ -137,16 +132,6 @@ public class LoginActionsService {
|
|||
return baseUriBuilder.path(RealmsResource.class).path(RealmsResource.class, "getLoginActionsService");
|
||||
}
|
||||
|
||||
public static UriBuilder processLoginUrl(UriInfo uriInfo) {
|
||||
UriBuilder baseUriBuilder = uriInfo.getBaseUriBuilder();
|
||||
return processLoginUrl(baseUriBuilder);
|
||||
}
|
||||
|
||||
public static UriBuilder processLoginUrl(UriBuilder baseUriBuilder) {
|
||||
UriBuilder uriBuilder = loginActionsBaseUrl(baseUriBuilder);
|
||||
return uriBuilder.path(OIDCLoginProtocolService.class, "processLogin");
|
||||
}
|
||||
|
||||
public static UriBuilder processOAuthUrl(UriInfo uriInfo) {
|
||||
UriBuilder baseUriBuilder = uriInfo.getBaseUriBuilder();
|
||||
return processOAuthUrl(baseUriBuilder);
|
||||
|
@ -179,10 +164,16 @@ public class LoginActionsService {
|
|||
boolean check(String code, String requiredAction) {
|
||||
if (!check(code)) {
|
||||
return false;
|
||||
} else if (!clientCode.isValid(requiredAction)) {
|
||||
} else if (!clientCode.isValidAction(requiredAction)) {
|
||||
event.client(clientCode.getClientSession().getClient());
|
||||
event.error(Errors.INVALID_CODE);
|
||||
response = ErrorPage.error(session, Messages.INVALID_CODE);
|
||||
return false;
|
||||
} else if (!clientCode.isActionActive(requiredAction)) {
|
||||
event.client(clientCode.getClientSession().getClient());
|
||||
event.error(Errors.EXPIRED_CODE);
|
||||
response = ErrorPage.error(session, Messages.INVALID_CODE);
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
@ -191,10 +182,16 @@ public class LoginActionsService {
|
|||
boolean check(String code, String requiredAction, String alternativeRequiredAction) {
|
||||
if (!check(code)) {
|
||||
return false;
|
||||
} else if (!(clientCode.isValid(requiredAction) || clientCode.isValid(alternativeRequiredAction))) {
|
||||
} else if (!(clientCode.isValidAction(requiredAction) || clientCode.isValidAction(alternativeRequiredAction))) {
|
||||
event.client(clientCode.getClientSession().getClient());
|
||||
event.error(Errors.INVALID_CODE);
|
||||
response = ErrorPage.error(session, Messages.INVALID_CODE);
|
||||
return false;
|
||||
} else if (!(clientCode.isActionActive(requiredAction) || clientCode.isActionActive(alternativeRequiredAction))) {
|
||||
event.client(clientCode.getClientSession().getClient());
|
||||
event.error(Errors.EXPIRED_CODE);
|
||||
response = ErrorPage.error(session, Messages.INVALID_CODE);
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
@ -217,6 +214,21 @@ public class LoginActionsService {
|
|||
response = ErrorPage.error(session, Messages.INVALID_CODE);
|
||||
return false;
|
||||
}
|
||||
ClientSessionModel clientSession = clientCode.getClientSession();
|
||||
event.detail(Details.CODE_ID, clientSession.getId());
|
||||
ClientModel client = clientSession.getClient();
|
||||
if (client == null) {
|
||||
event.error(Errors.CLIENT_NOT_FOUND);
|
||||
response = ErrorPage.error(session, Messages.UNKNOWN_LOGIN_REQUESTER);
|
||||
return false;
|
||||
}
|
||||
session.getContext().setClient(client);
|
||||
|
||||
if (!client.isEnabled()) {
|
||||
event.error(Errors.CLIENT_NOT_FOUND);
|
||||
response = ErrorPage.error(session, Messages.LOGIN_REQUESTER_NOT_ENABLED);
|
||||
return false;
|
||||
}
|
||||
session.getContext().setClient(clientCode.getClientSession().getClient());
|
||||
return true;
|
||||
}
|
||||
|
@ -246,9 +258,24 @@ public class LoginActionsService {
|
|||
clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
|
||||
}
|
||||
|
||||
return session.getProvider(LoginFormsProvider.class)
|
||||
.setClientSessionCode(clientSessionCode.getCode())
|
||||
.createLogin();
|
||||
AuthenticationFlowModel flow = realm.getFlowByAlias(DefaultAuthenticationFlows.BROWSER_FLOW);
|
||||
String flowId = flow.getId();
|
||||
AuthenticationProcessor processor = new AuthenticationProcessor();
|
||||
processor.setClientSession(clientSession)
|
||||
.setFlowId(flowId)
|
||||
.setConnection(clientConnection)
|
||||
.setEventBuilder(event)
|
||||
.setProtector(authManager.getProtector())
|
||||
.setRealm(realm)
|
||||
.setSession(session)
|
||||
.setUriInfo(uriInfo)
|
||||
.setRequest(request);
|
||||
|
||||
try {
|
||||
return processor.authenticate();
|
||||
} catch (Exception e) {
|
||||
return processor.handleBrowserException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -283,7 +310,7 @@ public class LoginActionsService {
|
|||
.createRegistration();
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* URL called after login page. YOU SHOULD NEVER INVOKE THIS DIRECTLY!
|
||||
*
|
||||
* @param code
|
||||
|
@ -295,51 +322,18 @@ public class LoginActionsService {
|
|||
public Response authForm(@QueryParam("code") String code,
|
||||
@QueryParam("action") String action) {
|
||||
event.event(EventType.LOGIN);
|
||||
if (!checkSsl()) {
|
||||
event.error(Errors.SSL_REQUIRED);
|
||||
return ErrorPage.error(session, Messages.HTTPS_REQUIRED);
|
||||
Checks checks = new Checks();
|
||||
if (!checks.check(code, ClientSessionModel.Action.AUTHENTICATE.name())) {
|
||||
return checks.response;
|
||||
}
|
||||
final ClientSessionCode clientCode = checks.clientCode;
|
||||
final ClientSessionModel clientSession = clientCode.getClientSession();
|
||||
|
||||
if (!realm.isEnabled()) {
|
||||
event.error(Errors.REALM_DISABLED);
|
||||
return ErrorPage.error(session, Messages.REALM_NOT_ENABLED);
|
||||
}
|
||||
ClientSessionCode clientCode = ClientSessionCode.parse(code, session, realm);
|
||||
if (clientCode == null) {
|
||||
event.error(Errors.INVALID_CODE);
|
||||
return ErrorPage.error(session, Messages.INVALID_CODE);
|
||||
}
|
||||
|
||||
ClientSessionModel clientSession = clientCode.getClientSession();
|
||||
event.detail(Details.CODE_ID, clientSession.getId());
|
||||
|
||||
if (!clientCode.isValid(ClientSessionModel.Action.AUTHENTICATE.name()) || clientSession.getUserSession() != null) {
|
||||
event.client(clientSession.getClient()).error(Errors.EXPIRED_CODE);
|
||||
return ErrorPage.error(session, Messages.EXPIRED_CODE);
|
||||
}
|
||||
|
||||
ClientModel client = clientSession.getClient();
|
||||
if (client == null) {
|
||||
event.error(Errors.CLIENT_NOT_FOUND);
|
||||
return ErrorPage.error(session, Messages.UNKNOWN_LOGIN_REQUESTER);
|
||||
}
|
||||
session.getContext().setClient(client);
|
||||
|
||||
if (!client.isEnabled()) {
|
||||
event.error(Errors.CLIENT_DISABLED);
|
||||
return ErrorPage.error(session, Messages.LOGIN_REQUESTER_NOT_ENABLED);
|
||||
}
|
||||
|
||||
String flowId = null;
|
||||
for (AuthenticationFlowModel flow : realm.getAuthenticationFlows()) {
|
||||
if (flow.getAlias().equals("browser")) {
|
||||
flowId = flow.getId();
|
||||
break;
|
||||
}
|
||||
}
|
||||
String flowAlias = DefaultAuthenticationFlows.BROWSER_FLOW;
|
||||
AuthenticationFlowModel flow = realm.getFlowByAlias(flowAlias);
|
||||
AuthenticationProcessor processor = new AuthenticationProcessor();
|
||||
processor.setClientSession(clientSession)
|
||||
.setFlowId(flowId)
|
||||
.setFlowId(flow.getId())
|
||||
.setConnection(clientConnection)
|
||||
.setEventBuilder(event)
|
||||
.setProtector(authManager.getProtector())
|
||||
|
@ -357,142 +351,6 @@ public class LoginActionsService {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* URL called after login page. YOU SHOULD NEVER INVOKE THIS DIRECTLY!
|
||||
*
|
||||
* @param code
|
||||
* @param formData
|
||||
* @return
|
||||
*/
|
||||
@Path("request/login")
|
||||
@POST
|
||||
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||
public Response processLogin(@QueryParam("code") String code,
|
||||
final MultivaluedMap<String, String> formData) {
|
||||
event.event(EventType.LOGIN);
|
||||
if (!checkSsl()) {
|
||||
event.error(Errors.SSL_REQUIRED);
|
||||
return ErrorPage.error(session, Messages.HTTPS_REQUIRED);
|
||||
}
|
||||
|
||||
if (!realm.isEnabled()) {
|
||||
event.error(Errors.REALM_DISABLED);
|
||||
return ErrorPage.error(session, Messages.REALM_NOT_ENABLED);
|
||||
}
|
||||
ClientSessionCode clientCode = ClientSessionCode.parse(code, session, realm);
|
||||
if (clientCode == null) {
|
||||
event.error(Errors.INVALID_CODE);
|
||||
return ErrorPage.error(session, Messages.INVALID_CODE);
|
||||
}
|
||||
|
||||
ClientSessionModel clientSession = clientCode.getClientSession();
|
||||
event.detail(Details.CODE_ID, clientSession.getId());
|
||||
|
||||
if (!clientCode.isValid(ClientSessionModel.Action.AUTHENTICATE.name()) || clientSession.getUserSession() != null) {
|
||||
clientCode.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
|
||||
event.client(clientSession.getClient()).error(Errors.EXPIRED_CODE);
|
||||
return session.getProvider(LoginFormsProvider.class)
|
||||
.setError(Messages.EXPIRED_CODE)
|
||||
.setClientSessionCode(clientCode.getCode())
|
||||
.createLogin();
|
||||
}
|
||||
|
||||
String username = formData.getFirst(AuthenticationManager.FORM_USERNAME);
|
||||
|
||||
String rememberMe = formData.getFirst("rememberMe");
|
||||
boolean remember = rememberMe != null && rememberMe.equalsIgnoreCase("on");
|
||||
|
||||
event.client(clientSession.getClient().getClientId())
|
||||
.detail(Details.REDIRECT_URI, clientSession.getRedirectUri())
|
||||
.detail(Details.RESPONSE_TYPE, "code")
|
||||
.detail(Details.AUTH_METHOD, "form")
|
||||
.detail(Details.USERNAME, username);
|
||||
|
||||
if (remember) {
|
||||
event.detail(Details.REMEMBER_ME, "true");
|
||||
}
|
||||
|
||||
ClientModel client = clientSession.getClient();
|
||||
if (client == null) {
|
||||
event.error(Errors.CLIENT_NOT_FOUND);
|
||||
return ErrorPage.error(session, Messages.UNKNOWN_LOGIN_REQUESTER);
|
||||
}
|
||||
if (!client.isEnabled()) {
|
||||
event.error(Errors.CLIENT_DISABLED);
|
||||
return ErrorPage.error(session, Messages.LOGIN_REQUESTER_NOT_ENABLED);
|
||||
}
|
||||
|
||||
session.getContext().setClient(clientSession.getClient());
|
||||
|
||||
if (formData.containsKey("cancel")) {
|
||||
event.error(Errors.REJECTED_BY_USER);
|
||||
LoginProtocol protocol = session.getProvider(LoginProtocol.class, clientSession.getAuthMethod());
|
||||
protocol.setRealm(realm)
|
||||
.setHttpHeaders(headers)
|
||||
.setUriInfo(uriInfo);
|
||||
return protocol.cancelLogin(clientSession);
|
||||
}
|
||||
|
||||
AuthenticationManager.AuthenticationStatus status = authManager.authenticateForm(session, clientConnection, realm, formData);
|
||||
|
||||
if (remember) {
|
||||
authManager.createRememberMeCookie(realm, username, uriInfo, clientConnection);
|
||||
} else {
|
||||
authManager.expireRememberMeCookie(realm, uriInfo, clientConnection);
|
||||
}
|
||||
|
||||
UserModel user = KeycloakModelUtils.findUserByNameOrEmail(session, realm, username);
|
||||
if (user != null) {
|
||||
event.user(user);
|
||||
}
|
||||
|
||||
switch (status) {
|
||||
case SUCCESS:
|
||||
case ACTIONS_REQUIRED:
|
||||
UserSessionModel userSession = session.sessions().createUserSession(realm, user, username, clientConnection.getRemoteAddr(), "form", remember, null, null);
|
||||
TokenManager.attachClientSession(userSession, clientSession);
|
||||
event.session(userSession);
|
||||
return authManager.nextActionAfterAuthentication(session, userSession, clientSession, clientConnection, request, uriInfo, event);
|
||||
case ACCOUNT_TEMPORARILY_DISABLED:
|
||||
event.error(Errors.USER_TEMPORARILY_DISABLED);
|
||||
return session.getProvider(LoginFormsProvider.class)
|
||||
.setError(Messages.ACCOUNT_TEMPORARILY_DISABLED)
|
||||
.setFormData(formData)
|
||||
.setClientSessionCode(clientCode.getCode())
|
||||
.createLogin();
|
||||
case ACCOUNT_DISABLED:
|
||||
event.error(Errors.USER_DISABLED);
|
||||
return session.getProvider(LoginFormsProvider.class)
|
||||
.setError(Messages.ACCOUNT_DISABLED)
|
||||
.setClientSessionCode(clientCode.getCode())
|
||||
.setFormData(formData).createLogin();
|
||||
case MISSING_TOTP:
|
||||
formData.remove(CredentialRepresentation.PASSWORD);
|
||||
|
||||
String passwordToken = new JWSBuilder().jsonContent(new PasswordToken(realm.getName(), user.getId())).rsa256(realm.getPrivateKey());
|
||||
formData.add(CredentialRepresentation.PASSWORD_TOKEN, passwordToken);
|
||||
|
||||
return session.getProvider(LoginFormsProvider.class)
|
||||
.setFormData(formData)
|
||||
.setClientSessionCode(clientCode.getCode())
|
||||
.createLoginTotp();
|
||||
case INVALID_USER:
|
||||
event.error(Errors.USER_NOT_FOUND);
|
||||
return session.getProvider(LoginFormsProvider.class)
|
||||
.setError(Messages.INVALID_USER)
|
||||
.setFormData(formData)
|
||||
.setClientSessionCode(clientCode.getCode())
|
||||
.createLogin();
|
||||
default:
|
||||
event.error(Errors.INVALID_USER_CREDENTIALS);
|
||||
return session.getProvider(LoginFormsProvider.class)
|
||||
.setError(Messages.INVALID_USER)
|
||||
.setFormData(formData)
|
||||
.setClientSessionCode(clientCode.getCode())
|
||||
.createLogin();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registration
|
||||
*
|
||||
|
@ -993,19 +851,14 @@ public class LoginActionsService {
|
|||
public Response sendPasswordReset(@QueryParam("code") String code,
|
||||
final MultivaluedMap<String, String> formData) {
|
||||
event.event(EventType.SEND_RESET_PASSWORD);
|
||||
if (!checkSsl()) {
|
||||
return ErrorPage.error(session, Messages.HTTPS_REQUIRED);
|
||||
Checks checks = new Checks();
|
||||
if (!checks.check(code)) {
|
||||
return checks.response;
|
||||
}
|
||||
if (!realm.isEnabled()) {
|
||||
event.error(Errors.REALM_DISABLED);
|
||||
return ErrorPage.error(session, Messages.REALM_NOT_ENABLED);
|
||||
}
|
||||
ClientSessionCode accessCode = ClientSessionCode.parse(code, session, realm);
|
||||
if (accessCode == null) {
|
||||
event.error(Errors.INVALID_CODE);
|
||||
return ErrorPage.error(session, Messages.INVALID_CODE);
|
||||
}
|
||||
ClientSessionModel clientSession = accessCode.getClientSession();
|
||||
final ClientSessionCode accessCode = checks.clientCode;
|
||||
final ClientSessionModel clientSession = accessCode.getClientSession();
|
||||
ClientModel client = clientSession.getClient();
|
||||
|
||||
|
||||
String username = formData.getFirst("username");
|
||||
if(username == null || username.isEmpty()) {
|
||||
|
@ -1016,16 +869,6 @@ public class LoginActionsService {
|
|||
.createPasswordReset();
|
||||
}
|
||||
|
||||
ClientModel client = clientSession.getClient();
|
||||
if (client == null) {
|
||||
return ErrorPage.error(session, Messages.UNKNOWN_LOGIN_REQUESTER);
|
||||
}
|
||||
if (!client.isEnabled()) {
|
||||
return ErrorPage.error(session, Messages.LOGIN_REQUESTER_NOT_ENABLED);
|
||||
}
|
||||
|
||||
session.getContext().setClient(client);
|
||||
|
||||
event.client(client.getClientId())
|
||||
.detail(Details.REDIRECT_URI, clientSession.getRedirectUri())
|
||||
.detail(Details.RESPONSE_TYPE, "code")
|
||||
|
@ -1114,43 +957,6 @@ public class LoginActionsService {
|
|||
public Object requiredAction(@QueryParam("code") String code,
|
||||
@PathParam("action") String action) {
|
||||
event.event(EventType.LOGIN);
|
||||
if (!checkSsl()) {
|
||||
event.error(Errors.SSL_REQUIRED);
|
||||
throw new WebApplicationException(ErrorPage.error(session, Messages.HTTPS_REQUIRED));
|
||||
}
|
||||
|
||||
if (!realm.isEnabled()) {
|
||||
event.error(Errors.REALM_DISABLED);
|
||||
return ErrorPage.error(session, Messages.REALM_NOT_ENABLED);
|
||||
}
|
||||
ClientSessionCode clientCode = ClientSessionCode.parse(code, session, realm);
|
||||
if (clientCode == null) {
|
||||
event.error(Errors.INVALID_CODE);
|
||||
throw new WebApplicationException(ErrorPage.error(session, Messages.INVALID_CODE));
|
||||
}
|
||||
|
||||
final ClientSessionModel clientSession = clientCode.getClientSession();
|
||||
event.detail(Details.CODE_ID, clientSession.getId());
|
||||
|
||||
/*
|
||||
if (!clientCode.isValid(ClientSessionModel.Action.AUTHENTICATE.name()) || clientSession.getUserSession() != null) {
|
||||
event.client(clientSession.getClient()).error(Errors.EXPIRED_CODE);
|
||||
throw new WebApplicationException(ErrorPage.error(session, Messages.EXPIRED_CODE));
|
||||
}
|
||||
*/
|
||||
|
||||
ClientModel client = clientSession.getClient();
|
||||
if (client == null) {
|
||||
event.error(Errors.CLIENT_NOT_FOUND);
|
||||
throw new WebApplicationException( ErrorPage.error(session, Messages.UNKNOWN_LOGIN_REQUESTER));
|
||||
}
|
||||
session.getContext().setClient(client);
|
||||
|
||||
if (!client.isEnabled()) {
|
||||
event.error(Errors.CLIENT_NOT_FOUND);
|
||||
throw new WebApplicationException( ErrorPage.error(session, Messages.LOGIN_REQUESTER_NOT_ENABLED));
|
||||
}
|
||||
|
||||
if (action == null) {
|
||||
logger.error("required action was null");
|
||||
event.error(Errors.INVALID_CODE);
|
||||
|
@ -1164,6 +970,20 @@ public class LoginActionsService {
|
|||
event.error(Errors.INVALID_CODE);
|
||||
throw new WebApplicationException(ErrorPage.error(session, Messages.INVALID_CODE));
|
||||
}
|
||||
Checks checks = new Checks();
|
||||
if (!checks.check(code, action)) {
|
||||
return checks.response;
|
||||
}
|
||||
final ClientSessionCode clientCode = checks.clientCode;
|
||||
final ClientSessionModel clientSession = clientCode.getClientSession();
|
||||
|
||||
if (clientSession.getUserSession() == null) {
|
||||
logger.error("user session was null");
|
||||
event.error(Errors.USER_SESSION_NOT_FOUND);
|
||||
throw new WebApplicationException(ErrorPage.error(session, Messages.SESSION_NOT_ACTIVE));
|
||||
}
|
||||
|
||||
|
||||
RequiredActionContext context = new RequiredActionContext() {
|
||||
@Override
|
||||
public EventBuilder getEvent() {
|
||||
|
@ -1209,7 +1029,14 @@ public class LoginActionsService {
|
|||
public HttpRequest getHttpRequest() {
|
||||
return request;
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public String generateAccessCode(String action) {
|
||||
ClientSessionCode code = new ClientSessionCode(getRealm(), getClientSession());
|
||||
code.setAction(action);
|
||||
return code.getCode();
|
||||
}
|
||||
};
|
||||
return provider.jaxrsService(context);
|
||||
|
||||
|
||||
|
|
|
@ -161,7 +161,7 @@ public class RealmsResource {
|
|||
public IdentityBrokerService getBrokerService(final @PathParam("realm") String name) {
|
||||
RealmModel realm = init(name);
|
||||
|
||||
IdentityBrokerService brokerService = new IdentityBrokerService(realm);
|
||||
IdentityBrokerService brokerService = new IdentityBrokerService(realm, protector);
|
||||
ResteasyProviderFactory.getInstance().injectProperties(brokerService);
|
||||
|
||||
brokerService.init();
|
||||
|
|
|
@ -334,7 +334,7 @@ public class AssertEvents implements TestRule, EventListenerProviderFactory {
|
|||
Assert.assertThat(actual.getSessionId(), sessionId);
|
||||
|
||||
if (details == null || details.isEmpty()) {
|
||||
Assert.assertNull(actual.getDetails());
|
||||
// Assert.assertNull(actual.getDetails());
|
||||
} else {
|
||||
Assert.assertNotNull(actual.getDetails());
|
||||
for (Map.Entry<String, Matcher<String>> d : details.entrySet()) {
|
||||
|
|
|
@ -178,7 +178,7 @@ public class ResetPasswordTest {
|
|||
|
||||
loginPage.login("login@test.com", "password");
|
||||
|
||||
Event loginEvent = events.expectLogin().user(userId).detail(Details.USERNAME, "login@test.com").assertEvent();
|
||||
Event loginEvent = events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").assertEvent();
|
||||
|
||||
String code = oauth.getCurrentQuery().get("code");
|
||||
OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(code, "password");
|
||||
|
@ -372,7 +372,7 @@ public class ResetPasswordTest {
|
|||
|
||||
assertEquals("An error occurred, please login again through your application.", errorPage.getError());
|
||||
|
||||
events.expectRequiredAction(EventType.RESET_PASSWORD).error("invalid_code").client((String) null).user((String) null).session((String) null).clearDetails().assertEvent();
|
||||
events.expectRequiredAction(EventType.RESET_PASSWORD).error("expired_code").client("test-app").user((String) null).session((String) null).clearDetails().assertEvent();
|
||||
} finally {
|
||||
Time.setOffset(0);
|
||||
}
|
||||
|
|
|
@ -152,20 +152,6 @@ public class AccessTokenPerfTest {
|
|||
return b.build(realm).toString();
|
||||
}
|
||||
|
||||
public String getProcessLoginUrl(String state) {
|
||||
UriBuilder b = LoginActionsService.processLoginUrl(UriBuilder.fromUri(baseUrl));
|
||||
if (clientId != null) {
|
||||
b.queryParam(OAuth2Constants.CLIENT_ID, clientId);
|
||||
}
|
||||
if (redirectUri != null) {
|
||||
b.queryParam(OAuth2Constants.REDIRECT_URI, redirectUri);
|
||||
}
|
||||
if (state != null) {
|
||||
b.queryParam(OAuth2Constants.STATE, state);
|
||||
}
|
||||
return b.build(realm).toString();
|
||||
}
|
||||
|
||||
static Pattern actionParser = Pattern.compile("action=\"([^\"]+)\"");
|
||||
|
||||
public void run() {
|
||||
|
|
Loading…
Reference in a new issue