diff --git a/docbook/reference/en/en-US/modules/auth-spi.xml b/docbook/reference/en/en-US/modules/auth-spi.xml index b5f7176871..e26643e800 100755 --- a/docbook/reference/en/en-US/modules/auth-spi.xml +++ b/docbook/reference/en/en-US/modules/auth-spi.xml @@ -624,8 +624,8 @@ public class SecretQuestionRequiredActionFactory implements RequiredActionFactor Enable Required Action The final thing you have to do is go into the admin console. Click on the Authentication left menu. - Click on the Required Actions tab. Find your required action, and enable. Alternatively, if you - click on the default action checkbox, this required action will be applied anytime a new user is created. + Click on the Required Actions tab. Click on the Register button and choose your new Required Action. + Your new required action should now be displayed and enabled in the required actions list. diff --git a/examples/providers/authenticator/README.md b/examples/providers/authenticator/README.md index 829bcfcc1e..5a8c50a833 100755 --- a/examples/providers/authenticator/README.md +++ b/examples/providers/authenticator/README.md @@ -14,13 +14,19 @@ Then registering the provider by editing keycloak-server.json and adding the mod ], -You then have to copy the secret-question.ftl file to the standalone/configuration/themes/base/login directory. +You then have to copy the secret-question.ftl and secret-question-config.ftl files to the standalone/configuration/themes/base/login directory. After you do all this, you then have to reboot keycloak. When reboot is complete, you will need to log into the admin console to create a new flow with your new authenticator. If you go to the Authentication menu item and go to the Flow tab, you will be able to view the currently defined flows. You cannot modify an built in flows, so, to add the Authenticator you -have to copy an existing flow or create your own. I'm hoping the UI is intuitive enough so that you -can figure out for yourself how to create a flow and add the Authenticator. We're looking to add a screencast +have to copy an existing flow or create your own. + +Next you have to register your required action. +Click on the Required Actions tab. Click on the Register button and choose your new Required Action. +Your new required action should now be displayed and enabled in the required actions list. + +I'm hoping the UI is intuitive enough so that you +can figure out for yourself how to create a flow and add the Authenticator and Required Action. We're looking to add a screencast to show this in action. diff --git a/examples/providers/authenticator/secret-question-config.ftl b/examples/providers/authenticator/secret-question-config.ftl new file mode 100755 index 0000000000..54e69026b0 --- /dev/null +++ b/examples/providers/authenticator/secret-question-config.ftl @@ -0,0 +1,33 @@ +<#import "template.ftl" as layout> +<@layout.registrationLayout; section> + <#if section = "title"> + ${msg("loginTitle",realm.name)} + <#elseif section = "header"> + Setup Secret Question + <#elseif section = "form"> +
+
+
+ +
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+ +
+
+
+
+ + \ No newline at end of file diff --git a/examples/providers/authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionAuthenticator.java b/examples/providers/authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionAuthenticator.java index 1f4a8aaa00..f70c0f7dc3 100755 --- a/examples/providers/authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionAuthenticator.java +++ b/examples/providers/authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionAuthenticator.java @@ -13,6 +13,8 @@ import org.keycloak.services.util.CookieHelper; import javax.ws.rs.core.Cookie; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriBuilder; +import java.net.URI; /** * @author Bill Burke @@ -24,7 +26,11 @@ public class SecretQuestionAuthenticator implements Authenticator { protected boolean hasCookie(AuthenticationFlowContext context) { Cookie cookie = context.getHttpRequest().getHttpHeaders().getCookies().get("SECRET_QUESTION_ANSWERED"); - return cookie != null; + boolean result = cookie != null; + if (result) { + System.out.println("Bypassing secret question because cookie as set"); + } + return result; } @Override @@ -33,12 +39,17 @@ public class SecretQuestionAuthenticator implements Authenticator { context.success(); return; } - Response challenge = context.form().createForm("secret_question.ftl"); + Response challenge = context.form().createForm("secret-question.ftl"); context.challenge(challenge); } @Override public void action(AuthenticationFlowContext context) { + MultivaluedMap formData = context.getHttpRequest().getDecodedFormParameters(); + if (formData.containsKey("cancel")) { + context.cancelLogin(); + return; + } boolean validated = validateAnswer(context); if (!validated) { Response challenge = context.form() @@ -58,11 +69,12 @@ public class SecretQuestionAuthenticator implements Authenticator { maxCookieAge = Integer.valueOf(config.getConfig().get("cookie.max.age")); } + URI uri = context.getUriInfo().getBaseUriBuilder().path("realms").path(context.getRealm().getName()).build(); CookieHelper.addCookie("SECRET_QUESTION_ANSWERED", "true", - context.getUriInfo().getBaseUri().getPath() + "/realms/" + context.getRealm().getName(), + uri.getRawPath(), null, null, maxCookieAge, - true, true); + false, true); } protected boolean validateAnswer(AuthenticationFlowContext context) { diff --git a/examples/providers/authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionRequiredAction.java b/examples/providers/authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionRequiredAction.java index d0e2de7123..8fa26eec3a 100755 --- a/examples/providers/authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionRequiredAction.java +++ b/examples/providers/authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionRequiredAction.java @@ -20,14 +20,14 @@ public class SecretQuestionRequiredAction implements RequiredActionProvider { @Override public void requiredActionChallenge(RequiredActionContext context) { - Response challenge = context.form().createForm("secret_question_config.ftl"); + Response challenge = context.form().createForm("secret-question-config.ftl"); context.challenge(challenge); } @Override public void processAction(RequiredActionContext context) { - String answer = (context.getHttpRequest().getDecodedFormParameters().getFirst("answer")); + String answer = (context.getHttpRequest().getDecodedFormParameters().getFirst("secret_answer")); UserCredentialValueModel model = new UserCredentialValueModel(); model.setValue(answer); model.setType(SecretQuestionAuthenticator.CREDENTIAL_TYPE); diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticationFlowContext.java b/services/src/main/java/org/keycloak/authentication/AuthenticationFlowContext.java index fd9803af39..26ec7bd862 100755 --- a/services/src/main/java/org/keycloak/authentication/AuthenticationFlowContext.java +++ b/services/src/main/java/org/keycloak/authentication/AuthenticationFlowContext.java @@ -217,4 +217,11 @@ public interface AuthenticationFlowContext { * @return */ URI getActionUrl(); + + /** + * End the flow and redirect browser based on protocol specific respones. This should only be executed + * in browser-based flows. + * + */ + void cancelLogin(); } diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java index aee300e1db..c7c80f4c1f 100755 --- a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java +++ b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java @@ -17,6 +17,7 @@ import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserSessionModel; +import org.keycloak.protocol.LoginProtocol; import org.keycloak.protocol.oidc.TokenManager; import org.keycloak.services.ErrorPage; import org.keycloak.services.managers.AuthenticationManager; @@ -367,6 +368,17 @@ public class AuthenticationProcessor { public URI getActionUrl() { return getActionUrl(generateAccessCode()); } + + @Override + public void cancelLogin() { + getEvent().error(Errors.REJECTED_BY_USER); + LoginProtocol protocol = getSession().getProvider(LoginProtocol.class, getClientSession().getAuthMethod()); + protocol.setRealm(getRealm()) + .setHttpHeaders(getHttpRequest().getHttpHeaders()) + .setUriInfo(getUriInfo()); + Response response = protocol.cancelLogin(getClientSession()); + forceChallenge(response); + } } public void logFailure() { diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/UsernamePasswordForm.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/UsernamePasswordForm.java index 9f42cf56cf..e8490eab7c 100755 --- a/services/src/main/java/org/keycloak/authentication/authenticators/browser/UsernamePasswordForm.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/UsernamePasswordForm.java @@ -26,13 +26,7 @@ public class UsernamePasswordForm extends AbstractUsernameFormAuthenticator impl public void action(AuthenticationFlowContext context) { MultivaluedMap formData = context.getHttpRequest().getDecodedFormParameters(); if (formData.containsKey("cancel")) { - context.getEvent().error(Errors.REJECTED_BY_USER); - LoginProtocol protocol = context.getSession().getProvider(LoginProtocol.class, context.getClientSession().getAuthMethod()); - protocol.setRealm(context.getRealm()) - .setHttpHeaders(context.getHttpRequest().getHttpHeaders()) - .setUriInfo(context.getUriInfo()); - Response response = protocol.cancelLogin(context.getClientSession()); - context.forceChallenge(response); + context.cancelLogin(); return; } if (!validateForm(context, formData)) { diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java index bcaa98e257..95d644eb82 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java @@ -167,7 +167,7 @@ public class AccountTest { }); } - @Test + //@Test public void ideTesting() throws Exception { Thread.sleep(100000000); }