parent
8238467c49
commit
07ab29378b
4 changed files with 62 additions and 41 deletions
|
@ -99,6 +99,8 @@ public class DefaultRequiredActions {
|
||||||
addUpdateLocaleAction(realm);
|
addUpdateLocaleAction(realm);
|
||||||
addDeleteAccountAction(realm);
|
addDeleteAccountAction(realm);
|
||||||
addUpdateEmailAction(realm);
|
addUpdateEmailAction(realm);
|
||||||
|
addWebAuthnRegisterAction(realm);
|
||||||
|
addWebAuthnPasswordlessRegisterAction(realm);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void addDeleteAccountAction(RealmModel realm) {
|
public static void addDeleteAccountAction(RealmModel realm) {
|
||||||
|
@ -140,4 +142,40 @@ public class DefaultRequiredActions {
|
||||||
realm.addRequiredActionProvider(updateEmail);
|
realm.addRequiredActionProvider(updateEmail);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void addWebAuthnRegisterAction(RealmModel realm) {
|
||||||
|
final String PROVIDER_ID = "webauthn-register";
|
||||||
|
|
||||||
|
final boolean isWebAuthnFeatureEnabled = Profile.isFeatureEnabled(Profile.Feature.WEB_AUTHN);
|
||||||
|
final boolean isRequiredActionActive = realm.getRequiredActionProviderByAlias(PROVIDER_ID) != null;
|
||||||
|
|
||||||
|
if (isWebAuthnFeatureEnabled && !isRequiredActionActive) {
|
||||||
|
final RequiredActionProviderModel webauthnRegister = new RequiredActionProviderModel();
|
||||||
|
webauthnRegister.setEnabled(true);
|
||||||
|
webauthnRegister.setAlias(PROVIDER_ID);
|
||||||
|
webauthnRegister.setName("Webauthn Register");
|
||||||
|
webauthnRegister.setProviderId(PROVIDER_ID);
|
||||||
|
webauthnRegister.setDefaultAction(false);
|
||||||
|
webauthnRegister.setPriority(70);
|
||||||
|
realm.addRequiredActionProvider(webauthnRegister);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addWebAuthnPasswordlessRegisterAction(RealmModel realm) {
|
||||||
|
final String PROVIDER_ID = "webauthn-register-passwordless";
|
||||||
|
|
||||||
|
final boolean isWebAuthnFeatureEnabled = Profile.isFeatureEnabled(Profile.Feature.WEB_AUTHN);
|
||||||
|
final boolean isRequiredActionActive = realm.getRequiredActionProviderByAlias(PROVIDER_ID) != null;
|
||||||
|
|
||||||
|
if (isWebAuthnFeatureEnabled && !isRequiredActionActive) {
|
||||||
|
final RequiredActionProviderModel webauthnRegister = new RequiredActionProviderModel();
|
||||||
|
webauthnRegister.setEnabled(true);
|
||||||
|
webauthnRegister.setAlias(PROVIDER_ID);
|
||||||
|
webauthnRegister.setName("Webauthn Register Passwordless");
|
||||||
|
webauthnRegister.setProviderId(PROVIDER_ID);
|
||||||
|
webauthnRegister.setDefaultAction(false);
|
||||||
|
webauthnRegister.setPriority(80);
|
||||||
|
realm.addRequiredActionProvider(webauthnRegister);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,6 +68,7 @@ import org.keycloak.testsuite.util.UserBuilder;
|
||||||
import org.keycloak.userprofile.UserProfileContext;
|
import org.keycloak.userprofile.UserProfileContext;
|
||||||
import org.keycloak.validate.validators.EmailValidator;
|
import org.keycloak.validate.validators.EmailValidator;
|
||||||
|
|
||||||
|
import javax.ws.rs.ClientErrorException;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -76,12 +77,14 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
|
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertThat;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -533,14 +536,28 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
||||||
requiredAction.setId("12345");
|
requiredAction.setId("12345");
|
||||||
requiredAction.setName(WebAuthnRegisterFactory.PROVIDER_ID);
|
requiredAction.setName(WebAuthnRegisterFactory.PROVIDER_ID);
|
||||||
requiredAction.setProviderId(WebAuthnRegisterFactory.PROVIDER_ID);
|
requiredAction.setProviderId(WebAuthnRegisterFactory.PROVIDER_ID);
|
||||||
testRealm().flows().registerRequiredAction(requiredAction);
|
|
||||||
|
try {
|
||||||
|
testRealm().flows().registerRequiredAction(requiredAction);
|
||||||
|
} catch (ClientErrorException e) {
|
||||||
|
assertThat(e.getResponse(), notNullValue());
|
||||||
|
assertThat(e.getResponse().getStatus(), is(409));
|
||||||
|
}
|
||||||
|
|
||||||
getCleanup().addRequiredAction(requiredAction.getProviderId());
|
getCleanup().addRequiredAction(requiredAction.getProviderId());
|
||||||
|
|
||||||
requiredAction = new RequiredActionProviderSimpleRepresentation();
|
requiredAction = new RequiredActionProviderSimpleRepresentation();
|
||||||
requiredAction.setId("6789");
|
requiredAction.setId("6789");
|
||||||
requiredAction.setName(WebAuthnPasswordlessRegisterFactory.PROVIDER_ID);
|
requiredAction.setName(WebAuthnPasswordlessRegisterFactory.PROVIDER_ID);
|
||||||
requiredAction.setProviderId(WebAuthnPasswordlessRegisterFactory.PROVIDER_ID);
|
requiredAction.setProviderId(WebAuthnPasswordlessRegisterFactory.PROVIDER_ID);
|
||||||
testRealm().flows().registerRequiredAction(requiredAction);
|
|
||||||
|
try {
|
||||||
|
testRealm().flows().registerRequiredAction(requiredAction);
|
||||||
|
} catch (ClientErrorException e) {
|
||||||
|
assertThat(e.getResponse(), notNullValue());
|
||||||
|
assertThat(e.getResponse().getStatus(), is(409));
|
||||||
|
}
|
||||||
|
|
||||||
getCleanup().addRequiredAction(requiredAction.getProviderId());
|
getCleanup().addRequiredAction(requiredAction.getProviderId());
|
||||||
|
|
||||||
List<AccountCredentialResource.CredentialContainer> credentials = getCredentials();
|
List<AccountCredentialResource.CredentialContainer> credentials = getCredentials();
|
||||||
|
|
|
@ -52,6 +52,8 @@ public class RequiredActionsTest extends AbstractAuthenticationTest {
|
||||||
addRequiredAction(expected, "delete_account", "Delete Account", false, false, null);
|
addRequiredAction(expected, "delete_account", "Delete Account", false, false, null);
|
||||||
addRequiredAction(expected, "terms_and_conditions", "Terms and Conditions", false, false, null);
|
addRequiredAction(expected, "terms_and_conditions", "Terms and Conditions", false, false, null);
|
||||||
addRequiredAction(expected, "update_user_locale", "Update User Locale", true, false, null);
|
addRequiredAction(expected, "update_user_locale", "Update User Locale", true, false, null);
|
||||||
|
addRequiredAction(expected, "webauthn-register", "Webauthn Register", true, false, null);
|
||||||
|
addRequiredAction(expected, "webauthn-register-passwordless", "Webauthn Register Passwordless", true, false, null);
|
||||||
|
|
||||||
compareRequiredActions(expected, sort(result));
|
compareRequiredActions(expected, sort(result));
|
||||||
|
|
||||||
|
@ -82,7 +84,7 @@ public class RequiredActionsTest extends AbstractAuthenticationTest {
|
||||||
|
|
||||||
// Dummy RequiredAction is not registered in the realm and WebAuthn actions
|
// Dummy RequiredAction is not registered in the realm and WebAuthn actions
|
||||||
List<RequiredActionProviderSimpleRepresentation> result = authMgmtResource.getUnregisteredRequiredActions();
|
List<RequiredActionProviderSimpleRepresentation> result = authMgmtResource.getUnregisteredRequiredActions();
|
||||||
Assert.assertEquals(4, result.size());
|
Assert.assertEquals(2, result.size());
|
||||||
RequiredActionProviderSimpleRepresentation action = result.stream().filter(
|
RequiredActionProviderSimpleRepresentation action = result.stream().filter(
|
||||||
a -> a.getProviderId().equals(DummyRequiredActionFactory.PROVIDER_ID)
|
a -> a.getProviderId().equals(DummyRequiredActionFactory.PROVIDER_ID)
|
||||||
).findFirst().get();
|
).findFirst().get();
|
||||||
|
|
|
@ -959,43 +959,16 @@ public class BrowserFlowTest extends AbstractTestRealmKeycloakTest {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This test checks that if a REQUIRED authentication execution which has isUserSetupAllowed -> true
|
|
||||||
* has its requiredActionProvider in a not registered state, then it will not try to create the required action,
|
|
||||||
* and will instead raise an credential setup required error.
|
|
||||||
* NOTE: webauthn currently isn't configured by default in the realm. When this changes, this test will need to be adapted
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
@AuthServerContainerExclude(REMOTE)
|
|
||||||
public void testLoginWithWithNoWebAuthnCredentialAndNoRequiredActionProviderRegistered(){
|
|
||||||
String newFlowAlias = "browser - copy 1";
|
|
||||||
configureBrowserFlowWithRequiredWebAuthn(newFlowAlias);
|
|
||||||
try {
|
|
||||||
provideUsernamePassword("test-user@localhost");
|
|
||||||
|
|
||||||
// Assert that the login evaluates to an error, as all required elements to not validate to successful
|
|
||||||
errorPage.assertCurrent();
|
|
||||||
|
|
||||||
} finally {
|
|
||||||
revertFlows("browser - copy 1");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This test checks that if a REQUIRED authentication execution which has isUserSetupAllowed -> true
|
* This test checks that if a REQUIRED authentication execution which has isUserSetupAllowed -> true
|
||||||
* has its requiredActionProvider disabled, then it will not try to create the required action,
|
* has its requiredActionProvider disabled, then it will not try to create the required action,
|
||||||
* and will instead raise an credential setup required error.
|
* and will instead raise an credential setup required error.
|
||||||
* NOTE: webauthn currently isn't configured by default in the realm. When this changes, this test will need to be adapted
|
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
@AuthServerContainerExclude(REMOTE)
|
@AuthServerContainerExclude(REMOTE)
|
||||||
public void testLoginWithWithNoWebAuthnCredentialAndRequiredActionProviderDisabled(){
|
public void testLoginWithWithNoWebAuthnCredentialAndRequiredActionProviderDisabled(){
|
||||||
String newFlowAlias = "browser - copy 1";
|
String newFlowAlias = "browser - copy 1";
|
||||||
configureBrowserFlowWithRequiredWebAuthn(newFlowAlias);
|
configureBrowserFlowWithRequiredWebAuthn(newFlowAlias);
|
||||||
RequiredActionProviderSimpleRepresentation requiredActionRepresentation = new RequiredActionProviderSimpleRepresentation();
|
|
||||||
requiredActionRepresentation.setName("WebAuthn Required Action");
|
|
||||||
requiredActionRepresentation.setProviderId(WebAuthnRegisterFactory.PROVIDER_ID);
|
|
||||||
testRealm().flows().registerRequiredAction(requiredActionRepresentation);
|
|
||||||
RequiredActionProviderRepresentation rapr = testRealm().flows().getRequiredAction(WebAuthnRegisterFactory.PROVIDER_ID);
|
RequiredActionProviderRepresentation rapr = testRealm().flows().getRequiredAction(WebAuthnRegisterFactory.PROVIDER_ID);
|
||||||
rapr.setEnabled(false);
|
rapr.setEnabled(false);
|
||||||
testRealm().flows().updateRequiredAction(WebAuthnRegisterFactory.PROVIDER_ID, rapr);
|
testRealm().flows().updateRequiredAction(WebAuthnRegisterFactory.PROVIDER_ID, rapr);
|
||||||
|
@ -1007,14 +980,12 @@ public class BrowserFlowTest extends AbstractTestRealmKeycloakTest {
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
revertFlows("browser - copy 1");
|
revertFlows("browser - copy 1");
|
||||||
testRealm().flows().removeRequiredAction(WebAuthnRegisterFactory.PROVIDER_ID);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This test checks that if a REQUIRED authentication execution which has isUserSetupAllowed -> true
|
* This test checks that if a REQUIRED authentication execution which has isUserSetupAllowed -> true
|
||||||
* has its requiredActionProvider enabled, than it will login and show the otpSetup page.
|
* has its requiredActionProvider enabled, then it will login and show the WebAuthn registration page.
|
||||||
* NOTE: webauthn currently isn't configured by default in the realm. When this changes, this test will need to be adapted
|
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
@AuthServerContainerExclude(REMOTE)
|
@AuthServerContainerExclude(REMOTE)
|
||||||
|
@ -1022,20 +993,13 @@ public class BrowserFlowTest extends AbstractTestRealmKeycloakTest {
|
||||||
String newFlowAlias = "browser - copy 1";
|
String newFlowAlias = "browser - copy 1";
|
||||||
configureBrowserFlowWithRequiredWebAuthn(newFlowAlias);
|
configureBrowserFlowWithRequiredWebAuthn(newFlowAlias);
|
||||||
|
|
||||||
RequiredActionProviderSimpleRepresentation requiredActionRepresentation = new RequiredActionProviderSimpleRepresentation();
|
|
||||||
requiredActionRepresentation.setName("WebAuthn Required Action");
|
|
||||||
requiredActionRepresentation.setProviderId(WebAuthnRegisterFactory.PROVIDER_ID);
|
|
||||||
testRealm().flows().registerRequiredAction(requiredActionRepresentation);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
provideUsernamePassword("test-user@localhost");
|
provideUsernamePassword("test-user@localhost");
|
||||||
|
|
||||||
// Assert that in this case you arrive to an webauthn setup
|
// Assert that in this case you arrive to an webauthn setup
|
||||||
Assert.assertTrue(driver.getCurrentUrl().contains("required-action?execution=" + WebAuthnRegisterFactory.PROVIDER_ID));
|
Assert.assertTrue(driver.getCurrentUrl().contains("required-action?execution=" + WebAuthnRegisterFactory.PROVIDER_ID));
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
revertFlows("browser - copy 1");
|
revertFlows("browser - copy 1");
|
||||||
testRealm().flows().removeRequiredAction(WebAuthnRegisterFactory.PROVIDER_ID);
|
|
||||||
UserRepresentation user = testRealm().users().search("test-user@localhost").get(0);
|
UserRepresentation user = testRealm().users().search("test-user@localhost").get(0);
|
||||||
user.setRequiredActions(Collections.emptyList());
|
user.setRequiredActions(Collections.emptyList());
|
||||||
testRealm().users().get(user.getId()).update(user);
|
testRealm().users().get(user.getId()).update(user);
|
||||||
|
|
Loading…
Reference in a new issue