totp refactor

This commit is contained in:
Bill Burke 2015-08-21 17:53:26 -04:00
parent c25967bd61
commit f245b67036
3 changed files with 46 additions and 58 deletions

View file

@ -5,7 +5,7 @@
<#elseif section = "header">
${msg("loginTotpTitle")}
<#elseif section = "form">
<form action="${url.loginUpdateTotpUrl}" class="${properties.kcFormClass!}" id="kc-totp-settings-form" method="post">
<form action="${url.loginAction}" class="${properties.kcFormClass!}" id="kc-totp-settings-form" method="post">
<div class="${properties.kcFormGroupClass!}">
<div class="${properties.kcLabelWrapperClass!}">
<label for="otp" class="${properties.kcLabelClass!}">${msg("loginTotpOneTime")}</label>

View file

@ -5,13 +5,23 @@ import org.keycloak.Config;
import org.keycloak.authentication.RequiredActionContext;
import org.keycloak.authentication.RequiredActionFactory;
import org.keycloak.authentication.RequiredActionProvider;
import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType;
import org.keycloak.login.LoginFormsProvider;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RequiredCredentialModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.utils.CredentialValidation;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.services.messages.Messages;
import org.keycloak.services.validation.Validation;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
/**
@ -26,16 +36,46 @@ public class UpdateTotp implements RequiredActionProvider, RequiredActionFactory
@Override
public void requiredActionChallenge(RequiredActionContext context) {
LoginFormsProvider loginFormsProvider = context.getSession().getProvider(LoginFormsProvider.class)
.setClientSessionCode(context.generateAccessCode(UserModel.RequiredAction.CONFIGURE_TOTP.name()))
.setUser(context.getUser());
Response challenge = loginFormsProvider.createResponse(UserModel.RequiredAction.CONFIGURE_TOTP);
Response challenge = context.form().createResponse(UserModel.RequiredAction.CONFIGURE_TOTP);
context.challenge(challenge);
}
@Override
public void processAction(RequiredActionContext context) {
context.failure();
EventBuilder event = context.getEvent();
event.event(EventType.UPDATE_TOTP);
MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
String totp = formData.getFirst("totp");
String totpSecret = formData.getFirst("totpSecret");
if (Validation.isBlank(totp)) {
Response challenge = context.form()
.setError(Messages.MISSING_TOTP)
.createResponse(UserModel.RequiredAction.CONFIGURE_TOTP);
context.challenge(challenge);
return;
} else if (!CredentialValidation.validOTP(context.getRealm(), totp, totpSecret)) {
Response challenge = context.form()
.setError(Messages.INVALID_TOTP)
.createResponse(UserModel.RequiredAction.CONFIGURE_TOTP);
context.challenge(challenge);
return;
}
UserCredentialModel credentials = new UserCredentialModel();
credentials.setType(context.getRealm().getOTPPolicy().getType());
credentials.setValue(totpSecret);
context.getSession().users().updateCredential(context.getRealm(), context.getUser(), credentials);
// if type is HOTP, to update counter we execute validation based on supplied token
UserCredentialModel cred = new UserCredentialModel();
cred.setType(context.getRealm().getOTPPolicy().getType());
cred.setValue(totp);
context.getSession().users().validCredentials(context.getRealm(), context.getUser(), cred);
context.getUser().setOtpEnabled(true);
context.success();
}

View file

@ -576,58 +576,6 @@ public class LoginActionsService {
return AuthenticationManager.nextActionAfterAuthentication(session, userSession, clientSession, clientConnection, request, uriInfo, event);
}
@Path("totp")
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response updateTotp(@QueryParam("code") String code,
final MultivaluedMap<String, String> formData) {
event.event(EventType.UPDATE_TOTP);
Checks checks = new Checks();
if (!checks.verifyCode(code, ClientSessionModel.Action.CONFIGURE_TOTP.name())) {
return checks.response;
}
ClientSessionCode accessCode = checks.clientCode;
ClientSessionModel clientSession = accessCode.getClientSession();
UserSessionModel userSession = clientSession.getUserSession();
UserModel user = userSession.getUser();
initEvent(clientSession);
String totp = formData.getFirst("totp");
String totpSecret = formData.getFirst("totpSecret");
LoginFormsProvider loginForms = session.getProvider(LoginFormsProvider.class).setUser(user);
if (Validation.isBlank(totp)) {
return loginForms.setError(Messages.MISSING_TOTP)
.setClientSessionCode(accessCode.getCode())
.createResponse(RequiredAction.CONFIGURE_TOTP);
} else if (!CredentialValidation.validOTP(realm, totp, totpSecret)) {
return loginForms.setError(Messages.INVALID_TOTP)
.setClientSessionCode(accessCode.getCode())
.createResponse(RequiredAction.CONFIGURE_TOTP);
}
UserCredentialModel credentials = new UserCredentialModel();
credentials.setType(realm.getOTPPolicy().getType());
credentials.setValue(totpSecret);
session.users().updateCredential(realm, user, credentials);
// if type is HOTP, to update counter we execute validation based on supplied token
UserCredentialModel cred = new UserCredentialModel();
cred.setType(realm.getOTPPolicy().getType());
cred.setValue(totp);
session.users().validCredentials(realm, user, cred);
user.setOtpEnabled(true);
user.removeRequiredAction(RequiredAction.CONFIGURE_TOTP);
event.clone().event(EventType.UPDATE_TOTP).success();
return AuthenticationManager.nextActionAfterAuthentication(session, userSession, clientSession, clientConnection, request, uriInfo, event);
}
@Path("email-verification")
@GET
public Response emailVerification(@QueryParam("code") String code, @QueryParam("key") String key) {