commit
a2171bf777
12 changed files with 266 additions and 30 deletions
|
@ -0,0 +1,47 @@
|
|||
package org.keycloak.representations;
|
||||
|
||||
import org.keycloak.util.Time;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class PasswordToken {
|
||||
|
||||
private String realm;
|
||||
private String user;
|
||||
private int timestamp;
|
||||
|
||||
public PasswordToken() {
|
||||
}
|
||||
|
||||
public PasswordToken(String realm, String user) {
|
||||
this.realm = realm;
|
||||
this.user = user;
|
||||
this.timestamp = Time.currentTime();
|
||||
}
|
||||
|
||||
public String getRealm() {
|
||||
return realm;
|
||||
}
|
||||
|
||||
public void setRealm(String realm) {
|
||||
this.realm = realm;
|
||||
}
|
||||
|
||||
public String getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUser(String user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public int getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(int timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
}
|
|
@ -7,6 +7,7 @@ package org.keycloak.representations.idm;
|
|||
public class CredentialRepresentation {
|
||||
public static final String SECRET = "secret";
|
||||
public static final String PASSWORD = "password";
|
||||
public static final String PASSWORD_TOKEN = "password-token";
|
||||
public static final String TOTP = "totp";
|
||||
public static final String CLIENT_CERT = "cert";
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<#elseif section = "form">
|
||||
<form id="kc-totp-login-form" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
|
||||
<input id="username" name="username" value="${login.username!''}" type="hidden" />
|
||||
<input id="password" name="password" value="${login.password!''}" type="hidden" />
|
||||
<input id="password-token" name="password-token" value="${login.passwordToken!''}" type="hidden" />
|
||||
|
||||
<div class="${properties.kcFormGroupClass!}">
|
||||
<div class="${properties.kcLabelWrapperClass!}">
|
||||
|
|
|
@ -32,10 +32,13 @@ public class LoginBean {
|
|||
|
||||
private String password;
|
||||
|
||||
private String passwordToken;
|
||||
|
||||
public LoginBean(MultivaluedMap<String, String> formData){
|
||||
if (formData != null) {
|
||||
username = formData.getFirst("username");
|
||||
password = formData.getFirst("password");
|
||||
passwordToken = formData.getFirst("password-token");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,4 +50,7 @@ public class LoginBean {
|
|||
return password;
|
||||
}
|
||||
|
||||
public String getPasswordToken() {
|
||||
return passwordToken;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import java.util.UUID;
|
|||
*/
|
||||
public class UserCredentialModel {
|
||||
public static final String PASSWORD = "password";
|
||||
public static final String PASSWORD_TOKEN = "password-token";
|
||||
|
||||
// Secret is same as password but it is not hashed
|
||||
public static final String SECRET = "secret";
|
||||
|
@ -27,6 +28,12 @@ public class UserCredentialModel {
|
|||
model.setValue(password);
|
||||
return model;
|
||||
}
|
||||
public static UserCredentialModel passwordToken(String passwordToken) {
|
||||
UserCredentialModel model = new UserCredentialModel();
|
||||
model.setType(PASSWORD_TOKEN);
|
||||
model.setValue(passwordToken);
|
||||
return model;
|
||||
}
|
||||
|
||||
public static UserCredentialModel secret(String password) {
|
||||
UserCredentialModel model = new UserCredentialModel();
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
package org.keycloak.models.utils;
|
||||
|
||||
import org.keycloak.jose.jws.JWSInput;
|
||||
import org.keycloak.jose.jws.crypto.RSAProvider;
|
||||
import org.keycloak.models.PasswordPolicy;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserCredentialValueModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.representations.PasswordToken;
|
||||
import org.keycloak.util.Time;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
@ -57,6 +62,28 @@ public class CredentialValidation {
|
|||
|
||||
}
|
||||
|
||||
public static boolean validPasswordToken(RealmModel realm, UserModel user, String encodedPasswordToken) {
|
||||
JWSInput jws = new JWSInput(encodedPasswordToken);
|
||||
if (!RSAProvider.verify(jws, realm.getPublicKey())) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
PasswordToken passwordToken = jws.readJsonContent(PasswordToken.class);
|
||||
if (!passwordToken.getRealm().equals(realm.getName())) {
|
||||
return false;
|
||||
}
|
||||
if (!passwordToken.getUser().equals(user.getId())) {
|
||||
return false;
|
||||
}
|
||||
if (Time.currentTime() - passwordToken.getTimestamp() > realm.getAccessCodeLifespanUserAction()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean validTOTP(RealmModel realm, UserModel user, String otp) {
|
||||
UserCredentialValueModel passwordCred = null;
|
||||
for (UserCredentialValueModel cred : user.getCredentialsDirectly()) {
|
||||
|
@ -114,6 +141,10 @@ public class CredentialValidation {
|
|||
if (!validPassword(realm, user, credential.getValue())) {
|
||||
return false;
|
||||
}
|
||||
} else if (credential.getType().equals(UserCredentialModel.PASSWORD_TOKEN)) {
|
||||
if (!validPasswordToken(realm, user, credential.getValue())) {
|
||||
return false;
|
||||
}
|
||||
} else if (credential.getType().equals(UserCredentialModel.TOTP)) {
|
||||
if (!validTOTP(realm, user, credential.getValue())) {
|
||||
return false;
|
||||
|
|
|
@ -265,29 +265,37 @@ public class AuthenticationManager {
|
|||
|
||||
if (types.contains(CredentialRepresentation.PASSWORD)) {
|
||||
List<UserCredentialModel> credentials = new LinkedList<UserCredentialModel>();
|
||||
|
||||
String password = formData.getFirst(CredentialRepresentation.PASSWORD);
|
||||
if (password == null) {
|
||||
if (password != null) {
|
||||
credentials.add(UserCredentialModel.password(password));
|
||||
}
|
||||
|
||||
String passwordToken = formData.getFirst(CredentialRepresentation.PASSWORD_TOKEN);
|
||||
if (passwordToken != null) {
|
||||
credentials.add(UserCredentialModel.passwordToken(passwordToken));
|
||||
}
|
||||
|
||||
String totp = formData.getFirst(CredentialRepresentation.TOTP);
|
||||
if (totp != null) {
|
||||
credentials.add(UserCredentialModel.totp(totp));
|
||||
}
|
||||
|
||||
if (password == null && passwordToken == null) {
|
||||
logger.debug("Password not provided");
|
||||
return AuthenticationStatus.MISSING_PASSWORD;
|
||||
}
|
||||
credentials.add(UserCredentialModel.password(password));
|
||||
|
||||
if (user.isTotp()) {
|
||||
String token = formData.getFirst(CredentialRepresentation.TOTP);
|
||||
if (token == null) {
|
||||
logger.debug("TOTP token not provided");
|
||||
return AuthenticationStatus.MISSING_TOTP;
|
||||
}
|
||||
credentials.add(UserCredentialModel.totp(token));
|
||||
|
||||
}
|
||||
|
||||
logger.debug("validating password for user: " + username);
|
||||
logger.debugv("validating password for user: {0}", username);
|
||||
|
||||
if (!session.users().validCredentials(realm, user, credentials)) {
|
||||
return AuthenticationStatus.INVALID_CREDENTIALS;
|
||||
}
|
||||
|
||||
if (user.isTotp() && totp == null) {
|
||||
return AuthenticationStatus.MISSING_TOTP;
|
||||
}
|
||||
|
||||
if (!user.getRequiredActions().isEmpty()) {
|
||||
return AuthenticationStatus.ACTIONS_REQUIRED;
|
||||
} else {
|
||||
|
|
|
@ -17,6 +17,7 @@ import org.keycloak.events.EventBuilder;
|
|||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.Errors;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.jose.jws.JWSBuilder;
|
||||
import org.keycloak.login.LoginFormsProvider;
|
||||
import org.keycloak.models.ApplicationModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
|
@ -38,6 +39,7 @@ import org.keycloak.services.ForbiddenException;
|
|||
import org.keycloak.services.managers.AccessCode;
|
||||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
import org.keycloak.services.managers.AuthenticationManager.AuthenticationStatus;
|
||||
import org.keycloak.representations.PasswordToken;
|
||||
import org.keycloak.services.managers.ResourceAdminManager;
|
||||
import org.keycloak.services.managers.TokenManager;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
|
@ -545,6 +547,11 @@ public class TokenService {
|
|||
event.error(Errors.USER_DISABLED);
|
||||
return Flows.forms(this.session, realm, client, uriInfo).setError(Messages.ACCOUNT_DISABLED).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 Flows.forms(this.session, realm, client, uriInfo).setFormData(formData).createLoginTotp();
|
||||
case INVALID_USER:
|
||||
event.error(Errors.USER_NOT_FOUND);
|
||||
|
|
|
@ -66,7 +66,6 @@ public class UserFederationResource {
|
|||
@Path("providers")
|
||||
@Produces("application/json")
|
||||
public List<UserFederationProviderFactoryRepresentation> getProviders() {
|
||||
logger.info("get provider list");
|
||||
auth.requireView();
|
||||
List<UserFederationProviderFactoryRepresentation> providers = new LinkedList<UserFederationProviderFactoryRepresentation>();
|
||||
for (ProviderFactory factory : session.getKeycloakSessionFactory().getProviderFactories(UserFederationProvider.class)) {
|
||||
|
@ -75,7 +74,6 @@ public class UserFederationResource {
|
|||
rep.setOptions(((UserFederationProviderFactory)factory).getConfigurationOptions());
|
||||
providers.add(rep);
|
||||
}
|
||||
logger.info("provider list.size() " + providers.size());
|
||||
return providers;
|
||||
}
|
||||
|
||||
|
@ -89,7 +87,6 @@ public class UserFederationResource {
|
|||
@Path("providers/{id}")
|
||||
@Produces("application/json")
|
||||
public UserFederationProviderFactoryRepresentation getProvider(@PathParam("id") String id) {
|
||||
logger.info("get provider list");
|
||||
auth.requireView();
|
||||
for (ProviderFactory factory : session.getKeycloakSessionFactory().getProviderFactories(UserFederationProvider.class)) {
|
||||
if (!factory.getId().equals(id)) {
|
||||
|
@ -113,7 +110,6 @@ public class UserFederationResource {
|
|||
@Path("instances")
|
||||
@Consumes("application/json")
|
||||
public Response createProviderInstance(UserFederationProviderRepresentation rep) {
|
||||
logger.info("createProvider");
|
||||
auth.requireManage();
|
||||
String displayName = rep.getDisplayName();
|
||||
if (displayName != null && displayName.trim().equals("")) {
|
||||
|
@ -136,7 +132,6 @@ public class UserFederationResource {
|
|||
@Path("instances/{id}")
|
||||
@Consumes("application/json")
|
||||
public void updateProviderInstance(@PathParam("id") String id, UserFederationProviderRepresentation rep) {
|
||||
logger.info("updateProvider");
|
||||
auth.requireManage();
|
||||
String displayName = rep.getDisplayName();
|
||||
if (displayName != null && displayName.trim().equals("")) {
|
||||
|
@ -158,7 +153,6 @@ public class UserFederationResource {
|
|||
@Path("instances/{id}")
|
||||
@Produces("application/json")
|
||||
public UserFederationProviderRepresentation getProviderInstance(@PathParam("id") String id) {
|
||||
logger.info("getProvider");
|
||||
auth.requireView();
|
||||
for (UserFederationProviderModel model : realm.getUserFederationProviders()) {
|
||||
if (model.getId().equals(id)) {
|
||||
|
@ -176,7 +170,6 @@ public class UserFederationResource {
|
|||
@DELETE
|
||||
@Path("instances/{id}")
|
||||
public void deleteProviderInstance(@PathParam("id") String id) {
|
||||
logger.info("deleteProvider");
|
||||
auth.requireManage();
|
||||
UserFederationProviderModel model = new UserFederationProviderModel(id, null, null, -1, null, -1, -1, 0);
|
||||
realm.removeUserFederationProvider(model);
|
||||
|
@ -194,7 +187,6 @@ public class UserFederationResource {
|
|||
@Produces("application/json")
|
||||
@NoCache
|
||||
public List<UserFederationProviderRepresentation> getUserFederationInstances() {
|
||||
logger.info("getUserFederationInstances");
|
||||
auth.requireManage();
|
||||
List<UserFederationProviderRepresentation> reps = new LinkedList<UserFederationProviderRepresentation>();
|
||||
for (UserFederationProviderModel model : realm.getUserFederationProviders()) {
|
||||
|
@ -213,7 +205,7 @@ public class UserFederationResource {
|
|||
@Path("sync/{id}")
|
||||
@NoCache
|
||||
public Response syncUsers(@PathParam("id") String providerId, @QueryParam("action") String action) {
|
||||
logger.info("triggerSync");
|
||||
logger.debug("Syncing users");
|
||||
auth.requireManage();
|
||||
|
||||
for (UserFederationProviderModel model : realm.getUserFederationProviders()) {
|
||||
|
|
|
@ -94,6 +94,8 @@ public class LoginTotpTest {
|
|||
|
||||
private TimeBasedOTP totp = new TimeBasedOTP();
|
||||
|
||||
private int lifespan;
|
||||
|
||||
@Before
|
||||
public void before() throws MalformedURLException {
|
||||
totp = new TimeBasedOTP();
|
||||
|
@ -133,14 +135,45 @@ public class LoginTotpTest {
|
|||
loginPage.open();
|
||||
loginPage.login("test-user@localhost", "invalid");
|
||||
|
||||
loginTotpPage.assertCurrent();
|
||||
|
||||
loginTotpPage.login(totp.generate("totpSecret"));
|
||||
|
||||
loginPage.assertCurrent();
|
||||
|
||||
Assert.assertEquals("Invalid username or password.", loginPage.getError());
|
||||
|
||||
events.expectLogin().error("invalid_user_credentials").removeDetail(Details.CODE_ID).session((String) null).assertEvent();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginWithTotpExpiredPasswordToken() throws Exception {
|
||||
try {
|
||||
keycloakRule.configure(new KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
lifespan = appRealm.getAccessCodeLifespanUserAction();
|
||||
appRealm.setAccessCodeLifespanUserAction(1);
|
||||
}
|
||||
});
|
||||
|
||||
loginPage.open();
|
||||
loginPage.login("test-user@localhost", "password");
|
||||
|
||||
loginTotpPage.assertCurrent();
|
||||
|
||||
Thread.sleep(2000);
|
||||
|
||||
loginTotpPage.login(totp.generate("totpSecret"));
|
||||
|
||||
loginPage.assertCurrent();
|
||||
Assert.assertEquals("Invalid username or password.", loginPage.getError());
|
||||
|
||||
events.expectLogin().error("invalid_user_credentials").removeDetail(Details.CODE_ID).session((String) null).assertEvent();
|
||||
} finally {
|
||||
keycloakRule.configure(new KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.setAccessCodeLifespanUserAction(lifespan);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,15 +6,19 @@ import org.junit.Assert;
|
|||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.ClientConnection;
|
||||
import org.keycloak.jose.jws.JWSBuilder;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserModel.RequiredAction;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.models.utils.TimeBasedOTP;
|
||||
import org.keycloak.representations.PasswordToken;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
import org.keycloak.services.managers.AuthenticationManager.AuthenticationStatus;
|
||||
import org.keycloak.services.managers.BruteForceProtector;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import java.util.UUID;
|
||||
|
@ -117,7 +121,7 @@ public class AuthenticationManagerTest extends AbstractModelTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void authFormWithToltpInvalidPassword() {
|
||||
public void authFormWithTotpInvalidPassword() {
|
||||
authFormWithTotp();
|
||||
|
||||
formData.remove(CredentialRepresentation.PASSWORD);
|
||||
|
@ -127,6 +131,16 @@ public class AuthenticationManagerTest extends AbstractModelTest {
|
|||
Assert.assertEquals(AuthenticationStatus.INVALID_CREDENTIALS, status);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authFormWithTotpMissingPassword() {
|
||||
authFormWithTotp();
|
||||
|
||||
formData.remove(CredentialRepresentation.PASSWORD);
|
||||
|
||||
AuthenticationStatus status = am.authenticateForm(session, dummyConnection, realm, formData);
|
||||
Assert.assertEquals(AuthenticationStatus.MISSING_PASSWORD, status);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authFormWithTotpInvalidTotp() {
|
||||
authFormWithTotp();
|
||||
|
@ -148,6 +162,92 @@ public class AuthenticationManagerTest extends AbstractModelTest {
|
|||
Assert.assertEquals(AuthenticationStatus.MISSING_TOTP, status);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authFormWithTotpPasswordToken() {
|
||||
realm.addRequiredCredential(CredentialRepresentation.TOTP);
|
||||
|
||||
String totpSecret = UUID.randomUUID().toString();
|
||||
|
||||
UserCredentialModel credential = new UserCredentialModel();
|
||||
credential.setType(CredentialRepresentation.TOTP);
|
||||
credential.setValue(totpSecret);
|
||||
|
||||
user.updateCredential(credential);
|
||||
|
||||
user.setTotp(true);
|
||||
|
||||
String token = otp.generate(totpSecret);
|
||||
|
||||
formData.add(CredentialRepresentation.TOTP, token);
|
||||
formData.remove(CredentialRepresentation.PASSWORD);
|
||||
|
||||
String passwordToken = new JWSBuilder().jsonContent(new PasswordToken(realm.getName(), user.getId())).rsa256(realm.getPrivateKey());
|
||||
formData.add(CredentialRepresentation.PASSWORD_TOKEN, passwordToken);
|
||||
|
||||
AuthenticationStatus status = am.authenticateForm(session, dummyConnection, realm, formData);
|
||||
Assert.assertEquals(AuthenticationStatus.SUCCESS, status);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authFormWithTotpPasswordTokenInvalidKey() {
|
||||
authFormWithTotpPasswordToken();
|
||||
|
||||
formData.remove(CredentialRepresentation.PASSWORD_TOKEN);
|
||||
String passwordToken = new JWSBuilder().jsonContent(new PasswordToken(realm.getName(), user.getId())).rsa256(realm.getPrivateKey());
|
||||
formData.add(CredentialRepresentation.PASSWORD_TOKEN, passwordToken);
|
||||
|
||||
KeycloakModelUtils.generateRealmKeys(realm);
|
||||
|
||||
AuthenticationStatus status = am.authenticateForm(session, dummyConnection, realm, formData);
|
||||
Assert.assertEquals(AuthenticationStatus.INVALID_CREDENTIALS, status);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authFormWithTotpPasswordTokenInvalidRealm() {
|
||||
authFormWithTotpPasswordToken();
|
||||
|
||||
formData.remove(CredentialRepresentation.PASSWORD_TOKEN);
|
||||
String passwordToken = new JWSBuilder().jsonContent(new PasswordToken("invalid", user.getId())).rsa256(realm.getPrivateKey());
|
||||
formData.add(CredentialRepresentation.PASSWORD_TOKEN, passwordToken);
|
||||
|
||||
AuthenticationStatus status = am.authenticateForm(session, dummyConnection, realm, formData);
|
||||
Assert.assertEquals(AuthenticationStatus.INVALID_CREDENTIALS, status);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authFormWithTotpPasswordTokenInvalidUser() {
|
||||
authFormWithTotpPasswordToken();
|
||||
|
||||
formData.remove(CredentialRepresentation.PASSWORD_TOKEN);
|
||||
String passwordToken = new JWSBuilder().jsonContent(new PasswordToken(realm.getName(), "invalid")).rsa256(realm.getPrivateKey());
|
||||
formData.add(CredentialRepresentation.PASSWORD_TOKEN, passwordToken);
|
||||
|
||||
AuthenticationStatus status = am.authenticateForm(session, dummyConnection, realm, formData);
|
||||
Assert.assertEquals(AuthenticationStatus.INVALID_CREDENTIALS, status);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authFormWithTotpPasswordTokenExpired() throws InterruptedException {
|
||||
int lifespan = realm.getAccessCodeLifespanUserAction();
|
||||
|
||||
try {
|
||||
authFormWithTotpPasswordToken();
|
||||
|
||||
realm.setAccessCodeLifespanUserAction(1);
|
||||
|
||||
formData.remove(CredentialRepresentation.PASSWORD_TOKEN);
|
||||
String passwordToken = new JWSBuilder().jsonContent(new PasswordToken(realm.getName(), "invalid")).rsa256(realm.getPrivateKey());
|
||||
formData.add(CredentialRepresentation.PASSWORD_TOKEN, passwordToken);
|
||||
|
||||
Thread.sleep(2000);
|
||||
|
||||
AuthenticationStatus status = am.authenticateForm(session, dummyConnection, realm, formData);
|
||||
Assert.assertEquals(AuthenticationStatus.INVALID_CREDENTIALS, status);
|
||||
} finally {
|
||||
realm.setAccessCodeLifespanUserAction(lifespan);
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
@Override
|
||||
public void before() throws Exception {
|
||||
|
@ -157,8 +257,9 @@ public class AuthenticationManagerTest extends AbstractModelTest {
|
|||
realm.setAccessCodeLifespan(100);
|
||||
realm.setEnabled(true);
|
||||
realm.setName("TestAuth");
|
||||
realm.setPrivateKeyPem("0234234");
|
||||
realm.setPublicKeyPem("0234234");
|
||||
|
||||
KeycloakModelUtils.generateRealmKeys(realm);
|
||||
|
||||
realm.setAccessTokenLifespan(1000);
|
||||
realm.addRequiredCredential(CredentialRepresentation.PASSWORD);
|
||||
|
||||
|
|
|
@ -33,6 +33,9 @@ public class LoginTotpPage extends AbstractPage {
|
|||
@FindBy(id = "totp")
|
||||
private WebElement totpInput;
|
||||
|
||||
@FindBy(id = "password-token")
|
||||
private WebElement passwordToken;
|
||||
|
||||
@FindBy(css = "input[type=\"submit\"]")
|
||||
private WebElement submitButton;
|
||||
|
||||
|
|
Loading…
Reference in a new issue