unfinished working auth spi
This commit is contained in:
parent
143d176dcd
commit
a2718a889d
10 changed files with 172 additions and 5 deletions
|
@ -45,6 +45,9 @@ public class UrlBean {
|
|||
}
|
||||
|
||||
public String getLoginAction() {
|
||||
if (this.actionuri != null) {
|
||||
return this.actionuri.toString();
|
||||
}
|
||||
return Urls.realmLoginAction(baseURI, realm).toString();
|
||||
}
|
||||
|
||||
|
|
|
@ -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())) {
|
||||
|
|
|
@ -52,6 +52,7 @@ public class LoginFormOTPAuthenticator extends LoginFormUsernameAuthenticator {
|
|||
context.failureChallenge(AuthenticationProcessor.Error.INVALID_CREDENTIALS, challengeResponse);
|
||||
return;
|
||||
}
|
||||
context.success();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -58,6 +58,7 @@ public class OTPFormAuthenticator implements Authenticator {
|
|||
context.failureChallenge(AuthenticationProcessor.Error.INVALID_CREDENTIALS, challengeResponse);
|
||||
return;
|
||||
}
|
||||
context.success();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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) {
|
||||
|
@ -274,6 +276,116 @@ public class LoginActionsService {
|
|||
* 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!
|
||||
*
|
||||
* @param code
|
||||
* @param formData
|
||||
* @return
|
||||
*/
|
||||
|
|
|
@ -3,3 +3,4 @@ org.keycloak.protocol.ProtocolMapperSpi
|
|||
org.keycloak.exportimport.ClientImportSpi
|
||||
org.keycloak.wellknown.WellKnownSpi
|
||||
org.keycloak.messages.MessagesSpi
|
||||
org.keycloak.authentication.AuthenticatorSpi
|
Loading…
Reference in a new issue