keycloak-memoizeit
Changes
examples/providers/authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionAuthenticator.java 20(+16 -4)
examples/providers/authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionRequiredAction.java 4(+2 -2)
Details
diff --git a/docbook/reference/en/en-US/modules/auth-spi.xml b/docbook/reference/en/en-US/modules/auth-spi.xml
index b5f7176..e26643e 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
<title>Enable Required Action</title>
<para>
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.
</para>
</section>
</section>
diff --git a/examples/providers/authenticator/README.md b/examples/providers/authenticator/README.md
index 829bcfc..5a8c50a 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 0000000..54e6902
--- /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">
+ <form id="kc-totp-login-form" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
+ <div class="${properties.kcFormGroupClass!}">
+ <div class="${properties.kcLabelWrapperClass!}">
+ <label for="totp" class="${properties.kcLabelClass!}">What is your mom's first name?</label>
+ </div>
+
+ <div class="${properties.kcInputWrapperClass!}">
+ <input id="totp" name="secret_answer" type="text" class="${properties.kcInputClass!}" />
+ </div>
+ </div>
+
+ <div class="${properties.kcFormGroupClass!}">
+ <div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
+ <div class="${properties.kcFormOptionsWrapperClass!}">
+ </div>
+ </div>
+
+ <div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
+ <div class="${properties.kcFormButtonsWrapperClass!}">
+ <input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" name="login" id="kc-login" type="submit" value="${msg("doSubmit")}"/>
+ </div>
+ </div>
+ </div>
+ </form>
+ </#if>
+</@layout.registrationLayout>
\ 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 1f4a8aa..f70c0f7 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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -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<String, String> 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 d0e2de7..8fa26ee 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 fd9803a..26ec7bd 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 aee300e..c7c80f4 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 9f42cf5..e8490ea 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<String, String> 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 bcaa98e..95d644e 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);
}