diff --git a/examples/providers/authenticator/README.md b/examples/providers/authenticator/README.md new file mode 100755 index 0000000000..ef612cc2a7 --- /dev/null +++ b/examples/providers/authenticator/README.md @@ -0,0 +1,23 @@ +Example Custom Authenticator +=================================================== + +1. First, Keycloak must be running. + +2. Execute the follow. This will build the example and deploy it + + $ mvn clean install wildfly:deploy + +3. Copy the secret-question.ftl and secret-question-config.ftl files to the themes/base/login directory. + +4. Login to admin console. Hit browser refresh if you are already logged in so that the new providers show up. + +5. 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. Copy the "Browser" flow. + +6. In your copy, click the "Actions" menu item and "Add Execution". Pick Secret Question + +7. Next you have to register the required action that you created. Click on the Required Actions tab in the Authenticaiton menu. + 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/pom.xml b/examples/providers/authenticator/pom.xml new file mode 100755 index 0000000000..a89c7f8bf6 --- /dev/null +++ b/examples/providers/authenticator/pom.xml @@ -0,0 +1,73 @@ + + + + + keycloak-examples-providers-parent + org.keycloak + 5.0.0-SNAPSHOT + + + Authenticator Example + + 4.0.0 + + authenticator-required-action-example + jar + + + + org.keycloak + keycloak-core + provided + + + org.keycloak + keycloak-server-spi + provided + + + org.keycloak + keycloak-server-spi-private + provided + + + org.jboss.logging + jboss-logging + provided + + + org.keycloak + keycloak-services + provided + + + + + authenticator-required-action-example + + + org.wildfly.plugins + wildfly-maven-plugin + + false + + + + + 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/secret-question.ftl b/examples/providers/authenticator/secret-question.ftl new file mode 100755 index 0000000000..b69c5206a2 --- /dev/null +++ b/examples/providers/authenticator/secret-question.ftl @@ -0,0 +1,34 @@ +<#import "template.ftl" as layout> +<@layout.registrationLayout; section> + <#if section = "title"> + ${msg("loginTitle",realm.name)} + <#elseif section = "header"> + ${msg("loginTitleHtml",realm.name)} + <#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 new file mode 100755 index 0000000000..73d821fbcd --- /dev/null +++ b/examples/providers/authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionAuthenticator.java @@ -0,0 +1,136 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.examples.authenticator; + +import org.jboss.resteasy.spi.HttpResponse; +import org.jboss.resteasy.spi.ResteasyProviderFactory; +import org.keycloak.authentication.AuthenticationFlowContext; +import org.keycloak.authentication.AuthenticationFlowError; +import org.keycloak.authentication.Authenticator; +import org.keycloak.common.util.ServerCookie; +import org.keycloak.models.AuthenticatorConfigModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserCredentialModel; +import org.keycloak.models.UserModel; + +import javax.ws.rs.core.Cookie; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; +import java.net.URI; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class SecretQuestionAuthenticator implements Authenticator { + + public static final String CREDENTIAL_TYPE = "secret_question"; + + protected boolean hasCookie(AuthenticationFlowContext context) { + Cookie cookie = context.getHttpRequest().getHttpHeaders().getCookies().get("SECRET_QUESTION_ANSWERED"); + boolean result = cookie != null; + if (result) { + System.out.println("Bypassing secret question because cookie is set"); + } + return result; + } + + @Override + public void authenticate(AuthenticationFlowContext context) { + if (hasCookie(context)) { + context.success(); + return; + } + 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() + .setError("badSecret") + .createForm("secret-question.ftl"); + context.failureChallenge(AuthenticationFlowError.INVALID_CREDENTIALS, challenge); + return; + } + setCookie(context); + context.success(); + } + + protected void setCookie(AuthenticationFlowContext context) { + AuthenticatorConfigModel config = context.getAuthenticatorConfig(); + int maxCookieAge = 60 * 60 * 24 * 30; // 30 days + if (config != null) { + maxCookieAge = Integer.valueOf(config.getConfig().get("cookie.max.age")); + + } + URI uri = context.getUriInfo().getBaseUriBuilder().path("realms").path(context.getRealm().getName()).build(); + addCookie("SECRET_QUESTION_ANSWERED", "true", + uri.getRawPath(), + null, null, + maxCookieAge, + false, true); + } + + public static void addCookie(String name, String value, String path, String domain, String comment, int maxAge, boolean secure, boolean httpOnly) { + HttpResponse response = ResteasyProviderFactory.getContextData(HttpResponse.class); + StringBuffer cookieBuf = new StringBuffer(); + ServerCookie.appendCookieValue(cookieBuf, 1, name, value, path, domain, comment, maxAge, secure, httpOnly); + String cookie = cookieBuf.toString(); + response.getOutputHeaders().add(HttpHeaders.SET_COOKIE, cookie); + } + + + protected boolean validateAnswer(AuthenticationFlowContext context) { + MultivaluedMap formData = context.getHttpRequest().getDecodedFormParameters(); + String secret = formData.getFirst("secret_answer"); + UserCredentialModel input = new UserCredentialModel(); + input.setType(SecretQuestionCredentialProvider.SECRET_QUESTION); + input.setValue(secret); + return context.getSession().userCredentialManager().isValid(context.getRealm(), context.getUser(), input); + } + + @Override + public boolean requiresUser() { + return true; + } + + @Override + public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) { + return session.userCredentialManager().isConfiguredFor(realm, user, SecretQuestionCredentialProvider.SECRET_QUESTION); + } + + @Override + public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) { + user.addRequiredAction(SecretQuestionRequiredAction.PROVIDER_ID); + } + + @Override + public void close() { + + } +} diff --git a/examples/providers/authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionAuthenticatorFactory.java b/examples/providers/authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionAuthenticatorFactory.java new file mode 100755 index 0000000000..77c93b6e23 --- /dev/null +++ b/examples/providers/authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionAuthenticatorFactory.java @@ -0,0 +1,119 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.examples.authenticator; + +import org.keycloak.Config; +import org.keycloak.authentication.Authenticator; +import org.keycloak.authentication.AuthenticatorFactory; +import org.keycloak.authentication.ConfigurableAuthenticatorFactory; +import org.keycloak.models.AuthenticationExecutionModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.provider.ProviderConfigProperty; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class SecretQuestionAuthenticatorFactory implements AuthenticatorFactory, ConfigurableAuthenticatorFactory { + + public static final String PROVIDER_ID = "secret-question-authenticator"; + private static final SecretQuestionAuthenticator SINGLETON = new SecretQuestionAuthenticator(); + + @Override + public String getId() { + return PROVIDER_ID; + } + + @Override + public Authenticator create(KeycloakSession session) { + return SINGLETON; + } + + private static AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = { + AuthenticationExecutionModel.Requirement.REQUIRED, + AuthenticationExecutionModel.Requirement.DISABLED + }; + @Override + public AuthenticationExecutionModel.Requirement[] getRequirementChoices() { + return REQUIREMENT_CHOICES; + } + + @Override + public boolean isUserSetupAllowed() { + return true; + } + + @Override + public boolean isConfigurable() { + return true; + } + + @Override + public List getConfigProperties() { + return configProperties; + } + + private static final List configProperties = new ArrayList(); + + static { + ProviderConfigProperty property; + property = new ProviderConfigProperty(); + property.setName("cookie.max.age"); + property.setLabel("Cookie Max Age"); + property.setType(ProviderConfigProperty.STRING_TYPE); + property.setHelpText("Max age in seconds of the SECRET_QUESTION_COOKIE."); + configProperties.add(property); + } + + + @Override + public String getHelpText() { + return "A secret question that a user has to answer. i.e. What is your mother's maiden name."; + } + + @Override + public String getDisplayType() { + return "Secret Question"; + } + + @Override + public String getReferenceCategory() { + return "Secret Question"; + } + + @Override + public void init(Config.Scope config) { + + } + + @Override + public void postInit(KeycloakSessionFactory factory) { + + } + + @Override + public void close() { + + } + + +} diff --git a/examples/providers/authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionCredentialProvider.java b/examples/providers/authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionCredentialProvider.java new file mode 100644 index 0000000000..76b3239ea9 --- /dev/null +++ b/examples/providers/authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionCredentialProvider.java @@ -0,0 +1,134 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.examples.authenticator; + +import org.keycloak.common.util.Time; +import org.keycloak.credential.CredentialInput; +import org.keycloak.credential.CredentialInputUpdater; +import org.keycloak.credential.CredentialInputValidator; +import org.keycloak.credential.CredentialModel; +import org.keycloak.credential.CredentialProvider; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserCredentialModel; +import org.keycloak.models.UserModel; +import org.keycloak.models.cache.CachedUserModel; +import org.keycloak.models.cache.OnUserCache; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class SecretQuestionCredentialProvider implements CredentialProvider, CredentialInputValidator, CredentialInputUpdater, OnUserCache { + public static final String SECRET_QUESTION = "SECRET_QUESTION"; + public static final String CACHE_KEY = SecretQuestionCredentialProvider.class.getName() + "." + SECRET_QUESTION; + + protected KeycloakSession session; + + public SecretQuestionCredentialProvider(KeycloakSession session) { + this.session = session; + } + + public CredentialModel getSecret(RealmModel realm, UserModel user) { + CredentialModel secret = null; + if (user instanceof CachedUserModel) { + CachedUserModel cached = (CachedUserModel)user; + secret = (CredentialModel)cached.getCachedWith().get(CACHE_KEY); + + } else { + List creds = session.userCredentialManager().getStoredCredentialsByType(realm, user, SECRET_QUESTION); + if (!creds.isEmpty()) secret = creds.get(0); + } + return secret; + } + + + @Override + public boolean updateCredential(RealmModel realm, UserModel user, CredentialInput input) { + if (!SECRET_QUESTION.equals(input.getType())) return false; + if (!(input instanceof UserCredentialModel)) return false; + UserCredentialModel credInput = (UserCredentialModel) input; + List creds = session.userCredentialManager().getStoredCredentialsByType(realm, user, SECRET_QUESTION); + if (creds.isEmpty()) { + CredentialModel secret = new CredentialModel(); + secret.setType(SECRET_QUESTION); + secret.setValue(credInput.getValue()); + secret.setCreatedDate(Time.currentTimeMillis()); + session.userCredentialManager().createCredential(realm ,user, secret); + } else { + creds.get(0).setValue(credInput.getValue()); + session.userCredentialManager().updateCredential(realm, user, creds.get(0)); + } + session.userCache().evict(realm, user); + return true; + } + + @Override + public void disableCredentialType(RealmModel realm, UserModel user, String credentialType) { + if (!SECRET_QUESTION.equals(credentialType)) return; + + List credentials = session.userCredentialManager().getStoredCredentialsByType(realm, user, SECRET_QUESTION); + for (CredentialModel cred : credentials) { + session.userCredentialManager().removeStoredCredential(realm, user, cred.getId()); + } + session.userCache().evict(realm, user); + } + + @Override + public Set getDisableableCredentialTypes(RealmModel realm, UserModel user) { + if (!session.userCredentialManager().getStoredCredentialsByType(realm, user, SECRET_QUESTION).isEmpty()) { + Set set = new HashSet<>(); + set.add(SECRET_QUESTION); + return set; + } else { + return Collections.EMPTY_SET; + } + + } + + @Override + public boolean supportsCredentialType(String credentialType) { + return SECRET_QUESTION.equals(credentialType); + } + + @Override + public boolean isConfiguredFor(RealmModel realm, UserModel user, String credentialType) { + if (!SECRET_QUESTION.equals(credentialType)) return false; + return getSecret(realm, user) != null; + } + + @Override + public boolean isValid(RealmModel realm, UserModel user, CredentialInput input) { + if (!SECRET_QUESTION.equals(input.getType())) return false; + if (!(input instanceof UserCredentialModel)) return false; + + String secret = getSecret(realm, user).getValue(); + + return secret != null && ((UserCredentialModel)input).getValue().equals(secret); + } + + @Override + public void onCache(RealmModel realm, CachedUserModel user, UserModel delegate) { + List creds = session.userCredentialManager().getStoredCredentialsByType(realm, user, SECRET_QUESTION); + if (!creds.isEmpty()) user.getCachedWith().put(CACHE_KEY, creds.get(0)); + } +} diff --git a/examples/providers/authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionCredentialProviderFactory.java b/examples/providers/authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionCredentialProviderFactory.java new file mode 100644 index 0000000000..98b65ae9df --- /dev/null +++ b/examples/providers/authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionCredentialProviderFactory.java @@ -0,0 +1,37 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.examples.authenticator; + +import org.keycloak.credential.CredentialProvider; +import org.keycloak.credential.CredentialProviderFactory; +import org.keycloak.models.KeycloakSession; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class SecretQuestionCredentialProviderFactory implements CredentialProviderFactory { + @Override + public String getId() { + return "secret-question"; + } + + @Override + public CredentialProvider create(KeycloakSession session) { + return new SecretQuestionCredentialProvider(session); + } +} 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 new file mode 100755 index 0000000000..cc1425e742 --- /dev/null +++ b/examples/providers/authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionRequiredAction.java @@ -0,0 +1,59 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.examples.authenticator; + +import org.keycloak.authentication.RequiredActionContext; +import org.keycloak.authentication.RequiredActionProvider; +import org.keycloak.models.UserCredentialModel; + +import javax.ws.rs.core.Response; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class SecretQuestionRequiredAction implements RequiredActionProvider { + public static final String PROVIDER_ID = "secret_question_config"; + + @Override + public void evaluateTriggers(RequiredActionContext context) { + + } + + @Override + public void requiredActionChallenge(RequiredActionContext context) { + Response challenge = context.form().createForm("secret-question-config.ftl"); + context.challenge(challenge); + + } + + @Override + public void processAction(RequiredActionContext context) { + String answer = (context.getHttpRequest().getDecodedFormParameters().getFirst("secret_answer")); + UserCredentialModel input = new UserCredentialModel(); + input.setType(SecretQuestionCredentialProvider.SECRET_QUESTION); + input.setValue(answer); + context.getSession().userCredentialManager().updateCredential(context.getRealm(), context.getUser(), input); + context.success(); + } + + @Override + public void close() { + + } +} diff --git a/examples/providers/authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionRequiredActionFactory.java b/examples/providers/authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionRequiredActionFactory.java new file mode 100755 index 0000000000..46ad4ebc75 --- /dev/null +++ b/examples/providers/authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionRequiredActionFactory.java @@ -0,0 +1,65 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.examples.authenticator; + +import org.keycloak.Config; +import org.keycloak.authentication.RequiredActionFactory; +import org.keycloak.authentication.RequiredActionProvider; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class SecretQuestionRequiredActionFactory implements RequiredActionFactory { + + private static final SecretQuestionRequiredAction SINGLETON = new SecretQuestionRequiredAction(); + + @Override + public RequiredActionProvider create(KeycloakSession session) { + return SINGLETON; + } + + + @Override + public String getId() { + return SecretQuestionRequiredAction.PROVIDER_ID; + } + + @Override + public String getDisplayText() { + return "Secret Question"; + } + + @Override + public void init(Config.Scope config) { + + } + + @Override + public void postInit(KeycloakSessionFactory factory) { + + } + + @Override + public void close() { + + } + +} diff --git a/examples/providers/authenticator/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory b/examples/providers/authenticator/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory new file mode 100755 index 0000000000..f288d3daa9 --- /dev/null +++ b/examples/providers/authenticator/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory @@ -0,0 +1,18 @@ +# +# Copyright 2016 Red Hat, Inc. and/or its affiliates +# and other contributors as indicated by the @author tags. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +org.keycloak.examples.authenticator.SecretQuestionAuthenticatorFactory \ No newline at end of file diff --git a/examples/providers/authenticator/src/main/resources/META-INF/services/org.keycloak.authentication.RequiredActionFactory b/examples/providers/authenticator/src/main/resources/META-INF/services/org.keycloak.authentication.RequiredActionFactory new file mode 100755 index 0000000000..034c2f17c2 --- /dev/null +++ b/examples/providers/authenticator/src/main/resources/META-INF/services/org.keycloak.authentication.RequiredActionFactory @@ -0,0 +1,18 @@ +# +# Copyright 2016 Red Hat, Inc. and/or its affiliates +# and other contributors as indicated by the @author tags. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +org.keycloak.examples.authenticator.SecretQuestionRequiredActionFactory \ No newline at end of file diff --git a/examples/providers/authenticator/src/main/resources/META-INF/services/org.keycloak.credential.CredentialProviderFactory b/examples/providers/authenticator/src/main/resources/META-INF/services/org.keycloak.credential.CredentialProviderFactory new file mode 100644 index 0000000000..a221e371cc --- /dev/null +++ b/examples/providers/authenticator/src/main/resources/META-INF/services/org.keycloak.credential.CredentialProviderFactory @@ -0,0 +1 @@ + org.keycloak.examples.authenticator.SecretQuestionCredentialProviderFactory \ No newline at end of file diff --git a/examples/providers/pom.xml b/examples/providers/pom.xml index dc9734c907..d567966db8 100755 --- a/examples/providers/pom.xml +++ b/examples/providers/pom.xml @@ -33,5 +33,6 @@ rest domain-extension + authenticator