keycloak-aplcache
Changes
services/src/main/java/org/keycloak/authentication/authenticators/LoginFormOTPAuthenticator.java 1(+1 -0)
services/src/main/java/org/keycloak/authentication/authenticators/LoginFormPasswordAuthenticator.java 3(+2 -1)
services/src/main/java/org/keycloak/authentication/authenticators/LoginFormUsernameAuthenticator.java 10(+9 -1)
Details
diff --git a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/UrlBean.java b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/UrlBean.java
index 4c25bb3..b4b3f16 100755
--- a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/UrlBean.java
+++ b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/UrlBean.java
@@ -45,6 +45,9 @@ public class UrlBean {
}
public String getLoginAction() {
+ if (this.actionuri != null) {
+ return this.actionuri.toString();
+ }
return Urls.realmLoginAction(baseURI, realm).toString();
}
diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
index 5920e10..1ebfa50 100755
--- a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
+++ b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
@@ -279,6 +279,10 @@ public class AuthenticationProcessor {
super(message, cause, enableSuppression, writableStackTrace);
this.error = error;
}
+
+ public Error getError() {
+ return error;
+ }
}
public void logUserFailure() {
@@ -389,6 +393,9 @@ public class AuthenticationProcessor {
if (model.getRequirement() == AuthenticationExecutionModel.Requirement.REQUIRED) throw new AuthException(Error.INVALID_CREDENTIALS);
clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.ATTEMPTED);
continue;
+ } else {
+ logger.error("Unknown result status");
+ throw new AuthException(Error.INTERNAL_ERROR);
}
}
return null;
@@ -398,7 +405,7 @@ public class AuthenticationProcessor {
public void validateUser(UserModel authenticatedUser) {
if (authenticatedUser != null) {
- if (!clientSession.getAuthenticatedUser().isEnabled()) throw new AuthException(Error.USER_DISABLED);
+ if (!authenticatedUser.isEnabled()) throw new AuthException(Error.USER_DISABLED);
}
if (realm.isBruteForceProtected()) {
if (protector.isTemporarilyDisabled(session, realm, authenticatedUser.getUsername())) {
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormOTPAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormOTPAuthenticator.java
index f2f2ef2..c678c42 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormOTPAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormOTPAuthenticator.java
@@ -52,6 +52,7 @@ public class LoginFormOTPAuthenticator extends LoginFormUsernameAuthenticator {
context.failureChallenge(AuthenticationProcessor.Error.INVALID_CREDENTIALS, challengeResponse);
return;
}
+ context.success();
}
@Override
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormPasswordAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormPasswordAuthenticator.java
index bcc4c5e..17419a1 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormPasswordAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormPasswordAuthenticator.java
@@ -53,6 +53,7 @@ public class LoginFormPasswordAuthenticator extends LoginFormUsernameAuthenticat
context.failureChallenge(AuthenticationProcessor.Error.INVALID_CREDENTIALS, challengeResponse);
return;
}
+ context.success();
}
@Override
@@ -62,7 +63,7 @@ public class LoginFormPasswordAuthenticator extends LoginFormUsernameAuthenticat
@Override
public boolean configuredFor(UserModel user) {
- return false;
+ return user.configuredForCredentialType(UserCredentialModel.PASSWORD);
}
@Override
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormUsernameAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormUsernameAuthenticator.java
index 78b9096..56707f9 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormUsernameAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormUsernameAuthenticator.java
@@ -1,6 +1,7 @@
package org.keycloak.authentication.authenticators;
import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
+import org.keycloak.OAuth2Constants;
import org.keycloak.authentication.AuthenticationProcessor;
import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.AuthenticatorContext;
@@ -51,7 +52,9 @@ public class LoginFormUsernameAuthenticator implements Authenticator {
protected boolean isActionUrl(AuthenticatorContext context) {
URI expected = LoginActionsService.authenticationFormProcessor(context.getUriInfo()).build(context.getRealm().getName());
- return expected.getPath().equals(context.getUriInfo().getPath());
+ String current = context.getUriInfo().getAbsolutePath().getPath();
+ String expectedPath = expected.getPath();
+ return expectedPath.equals(current);
}
@@ -71,7 +74,11 @@ public class LoginFormUsernameAuthenticator implements Authenticator {
protected LoginFormsProvider loginForm(AuthenticatorContext context) {
ClientSessionCode code = new ClientSessionCode(context.getRealm(), context.getClientSession());
code.setAction(ClientSessionModel.Action.AUTHENTICATE);
+ URI action = LoginActionsService.authenticationFormProcessor(context.getUriInfo())
+ .queryParam(OAuth2Constants.CODE, code.getCode())
+ .build(context.getRealm().getName());
return context.getSession().getProvider(LoginFormsProvider.class)
+ .setActionUri(action)
.setClientSessionCode(code.getCode());
}
@@ -98,6 +105,7 @@ public class LoginFormUsernameAuthenticator implements Authenticator {
UserModel user = KeycloakModelUtils.findUserByNameOrEmail(context.getSession(), context.getRealm(), username);
if (invalidUser(context, user)) return;
context.setUser(user);
+ context.success();
}
public boolean invalidUser(AuthenticatorContext context, UserModel user) {
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/OTPFormAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/OTPFormAuthenticator.java
index 7659c92..d93836f 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/OTPFormAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/OTPFormAuthenticator.java
@@ -58,6 +58,7 @@ public class OTPFormAuthenticator implements Authenticator {
context.failureChallenge(AuthenticationProcessor.Error.INVALID_CREDENTIALS, challengeResponse);
return;
}
+ context.success();
}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java
index 93bf67b..fdb029e 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java
@@ -5,12 +5,15 @@ import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.ClientConnection;
import org.keycloak.OAuth2Constants;
+import org.keycloak.authentication.AuthenticationProcessor;
+import org.keycloak.authentication.authenticators.AuthenticationFlow;
import org.keycloak.constants.AdapterConstants;
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.IdentityProviderModel;
@@ -244,6 +247,32 @@ public class AuthorizationEndpoint {
return buildRedirectToIdentityProvider(idpHint, accessCode);
}
+ return oldBrowserAuthentication(accessCode);
+ }
+
+ protected Response newBrowserAuthentication(String accessCode) {
+ String flowId = null;
+ for (AuthenticationFlowModel flow : realm.getAuthenticationFlows()) {
+ if (flow.getAlias().equals("browser")) {
+ flowId = flow.getId();
+ break;
+ }
+ }
+ AuthenticationProcessor processor = new AuthenticationProcessor();
+ processor.setClientSession(clientSession)
+ .setFlowId(flowId)
+ .setConnection(clientConnection)
+ .setEventBuilder(event)
+ .setProtector(authManager.getProtector())
+ .setRealm(realm)
+ .setSession(session)
+ .setUriInfo(uriInfo)
+ .setRequest(request);
+
+ return processor.authenticate();
+ }
+
+ protected Response oldBrowserAuthentication(String accessCode) {
Response response = authManager.checkNonFormAuthentication(session, clientSession, realm, uriInfo, request, clientConnection, headers, event);
if (response != null) return response;
diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
index 60a49d5..3ada879 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -78,6 +78,10 @@ public class AuthenticationManager {
this.protector = protector;
}
+ public BruteForceProtector getProtector() {
+ return protector;
+ }
+
public static boolean isSessionValid(RealmModel realm, UserSessionModel userSession) {
if (userSession == null) {
logger.debug("No user session");
diff --git a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
index d1545f2..4329591 100755
--- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
+++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
@@ -24,6 +24,7 @@ package org.keycloak.services.resources;
import org.jboss.logging.Logger;
import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.ClientConnection;
+import org.keycloak.authentication.AuthenticationProcessor;
import org.keycloak.email.EmailException;
import org.keycloak.email.EmailProvider;
import org.keycloak.events.Details;
@@ -32,6 +33,7 @@ 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.AuthenticationFlowModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.RoleModel;
@@ -118,7 +120,7 @@ public class LoginActionsService {
}
public static UriBuilder authenticationFormProcessor(UriInfo uriInfo) {
- return loginActionsBaseUrl(uriInfo).path("auth-form");
+ return loginActionsBaseUrl(uriInfo).path(LoginActionsService.class, "authForm");
}
public static UriBuilder loginActionsBaseUrl(UriBuilder baseUriBuilder) {
@@ -270,6 +272,116 @@ public class LoginActionsService {
.createRegistration();
}
+ /**
+ * URL called after login page. YOU SHOULD NEVER INVOKE THIS DIRECTLY!
+ *
+ * @param code
+ * @return
+ */
+ @Path("auth-form")
+ @POST
+ @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+ public Response authForm(@QueryParam("code") String code) {
+ 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) || clientSession.getUserSession() != null) {
+ clientCode.setAction(ClientSessionModel.Action.AUTHENTICATE);
+ event.client(clientSession.getClient()).error(Errors.EXPIRED_CODE);
+ return session.getProvider(LoginFormsProvider.class)
+ .setError(Messages.EXPIRED_CODE)
+ .setClientSessionCode(clientCode.getCode())
+ .createLogin();
+ }
+
+ 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_NOT_FOUND);
+ 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;
+ }
+ }
+ 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 (AuthenticationProcessor.AuthException e) {
+ return handleError(e, code);
+ } catch (Exception e) {
+ logger.error("failed authentication", e);
+ return ErrorPage.error(session, Messages.UNEXPECTED_ERROR_HANDLING_RESPONSE);
+
+ }
+
+ }
+
+ protected Response handleError(AuthenticationProcessor.AuthException e, String code) {
+ logger.error("failed authentication: " + e.getError().toString(), e);
+ if (e.getError() == AuthenticationProcessor.Error.INVALID_USER) {
+ event.error(Errors.USER_NOT_FOUND);
+ return session.getProvider(LoginFormsProvider.class)
+ .setError(Messages.INVALID_USER)
+ .setClientSessionCode(code)
+ .createLogin();
+
+ } else if (e.getError() == AuthenticationProcessor.Error.USER_DISABLED) {
+ event.error(Errors.USER_DISABLED);
+ return session.getProvider(LoginFormsProvider.class)
+ .setError(Messages.ACCOUNT_DISABLED)
+ .setClientSessionCode(code)
+ .createLogin();
+
+ } else if (e.getError() == AuthenticationProcessor.Error.USER_TEMPORARILY_DISABLED) {
+ event.error(Errors.USER_TEMPORARILY_DISABLED);
+ return session.getProvider(LoginFormsProvider.class)
+ .setError(Messages.ACCOUNT_TEMPORARILY_DISABLED)
+ .setClientSessionCode(code)
+ .createLogin();
+
+ } else {
+ event.error(Errors.INVALID_USER_CREDENTIALS);
+ return session.getProvider(LoginFormsProvider.class)
+ .setError(Messages.INVALID_USER)
+ .setClientSessionCode(code)
+ .createLogin();
+ }
+ }
+
/**
* URL called after login page. YOU SHOULD NEVER INVOKE THIS DIRECTLY!
*
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
index a9b5a54..d2b5ca7 100755
--- a/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
+++ b/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
@@ -2,4 +2,5 @@ org.keycloak.protocol.LoginProtocolSpi
org.keycloak.protocol.ProtocolMapperSpi
org.keycloak.exportimport.ClientImportSpi
org.keycloak.wellknown.WellKnownSpi
-org.keycloak.messages.MessagesSpi
\ No newline at end of file
+org.keycloak.messages.MessagesSpi
+org.keycloak.authentication.AuthenticatorSpi
\ No newline at end of file