Move authenticator example to quickstarts (#27850)

Signed-off-by: stianst <stianst@gmail.com>
This commit is contained in:
Stian Thorgersen 2024-03-13 12:52:29 +01:00 committed by GitHub
parent 1099f03fe6
commit 1f772d2957
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 1 additions and 924 deletions

View file

@ -151,7 +151,7 @@ Let's walk through the steps from when a client first redirects to keycloak to a
In this section, we'll take a look at the Authenticator interface.
For this, we are going to implement an authenticator that requires that a user enter in the answer to a secret question like "What is your mother's maiden name?".
This example is fully implemented and contained in the examples/providers/authenticator directory of the demo distribution of {project_name}.
This example is fully implemented and contained in the {quickstartRepo_link}[{quickstartRepo_name}] repository under `extension/authenticator`.
To create an authenticator, you must at minimum implement the org.keycloak.authentication.AuthenticatorFactory and Authenticator interfaces.
The Authenticator interface defines the logic. The AuthenticatorFactory is responsible for creating instances of an Authenticator.

View file

@ -1,29 +0,0 @@
Example Custom Authenticator
===================================================
1. First, Keycloak must be running. See [Getting Started](https://github.com/keycloak/keycloak#getting-started), or you
can build distribution from [source](https://github.com/keycloak/keycloak/blob/main/docs/building.md).
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` server 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 **Flows** tab, you will be able to view the currently
defined flows. You cannot modify a 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 in **Forms** subflow and **Add Execution**. Pick `Secret Question` and change
the **Requirement** choice.
7. Go to the **Bindings** tab in **Authentication** menu and change the default **Browser Flow** to your copy of the browser flow
and click `Save`.
8. Next you have to register the required action that you created. Click on the **Required Actions** tab in the **Authentication** menu.
Click on the `Register` button and choose your new Required Action. You can also choose the `Default Action` for the Required Action
and each new user has to set the secret answer.
Your new required action should now be displayed and enabled in the required actions list.

View file

@ -1,73 +0,0 @@
<!--
~ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-examples-providers-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>999.0.0-SNAPSHOT</version>
</parent>
<name>Authenticator Example</name>
<description/>
<modelVersion>4.0.0</modelVersion>
<artifactId>authenticator-required-action-example</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-server-spi</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-server-spi-private</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-services</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>authenticator-required-action-example</finalName>
<plugins>
<plugin>
<groupId>org.wildfly.plugins</groupId>
<artifactId>wildfly-maven-plugin</artifactId>
<configuration>
<skip>false</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>

View file

@ -1,33 +0,0 @@
<#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>

View file

@ -1,35 +0,0 @@
<#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">
<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 type="hidden" id="id-hidden-input" name="credentialId" <#if auth.selectedCredential?has_content>value="${auth.selectedCredential}"</#if>/>
<input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"
name="login" id="kc-login" type="submit" value="${msg("doLogIn")}"/>
</div>
</div>
</div>
</form>
</#if>
</@layout.registrationLayout>

View file

@ -1,153 +0,0 @@
/*
* 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.http.HttpResponse;
import org.keycloak.authentication.AuthenticationFlowContext;
import org.keycloak.authentication.AuthenticationFlowError;
import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.CredentialValidator;
import org.keycloak.authentication.RequiredActionFactory;
import org.keycloak.authentication.RequiredActionProvider;
import org.keycloak.credential.CredentialProvider;
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 jakarta.ws.rs.core.Cookie;
import jakarta.ws.rs.core.MultivaluedMap;
import jakarta.ws.rs.core.NewCookie;
import jakarta.ws.rs.core.Response;
import java.net.URI;
import java.util.Collections;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class SecretQuestionAuthenticator implements Authenticator, CredentialValidator<SecretQuestionCredentialProvider> {
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) {
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(context, "SECRET_QUESTION_ANSWERED", "true",
uri.getRawPath(),
null, null,
maxCookieAge,
false, true);
}
public void addCookie(AuthenticationFlowContext context, String name, String value, String path, String domain, String comment, int maxAge, boolean secure, boolean httpOnly) {
HttpResponse response = context.getSession().getContext().getHttpResponse();
response.setCookieIfAbsent(new NewCookie.Builder(name)
.version(1)
.path(path)
.domain(domain)
.comment(comment)
.maxAge(maxAge)
.secure(secure)
.httpOnly(httpOnly)
.sameSite(null)
.build());
}
protected boolean validateAnswer(AuthenticationFlowContext context) {
MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
String secret = formData.getFirst("secret_answer");
String credentialId = formData.getFirst("credentialId");
if (credentialId == null || credentialId.isEmpty()) {
credentialId = getCredentialProvider(context.getSession())
.getDefaultCredential(context.getSession(), context.getRealm(), context.getUser()).getId();
}
UserCredentialModel input = new UserCredentialModel(credentialId, getType(context.getSession()), secret);
return getCredentialProvider(context.getSession()).isValid(context.getRealm(), context.getUser(), input);
}
@Override
public boolean requiresUser() {
return true;
}
@Override
public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
return getCredentialProvider(session).isConfiguredFor(realm, user, getType(session));
}
@Override
public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
user.addRequiredAction(SecretQuestionRequiredAction.PROVIDER_ID);
}
public List<RequiredActionFactory> getRequiredActions(KeycloakSession session) {
return Collections.singletonList((SecretQuestionRequiredActionFactory)session.getKeycloakSessionFactory().getProviderFactory(RequiredActionProvider.class, SecretQuestionRequiredAction.PROVIDER_ID));
}
@Override
public void close() {
}
@Override
public SecretQuestionCredentialProvider getCredentialProvider(KeycloakSession session) {
return (SecretQuestionCredentialProvider)session.getProvider(CredentialProvider.class, SecretQuestionCredentialProviderFactory.PROVIDER_ID);
}
}

View file

@ -1,120 +0,0 @@
/*
* 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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @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.ALTERNATIVE,
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<ProviderConfigProperty> getConfigProperties() {
return configProperties;
}
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
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() {
}
}

View file

@ -1,109 +0,0 @@
/*
* 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.logging.Logger;
import org.keycloak.common.util.Time;
import org.keycloak.credential.CredentialInput;
import org.keycloak.credential.CredentialInputValidator;
import org.keycloak.credential.CredentialModel;
import org.keycloak.credential.CredentialProvider;
import org.keycloak.credential.CredentialTypeMetadata;
import org.keycloak.credential.CredentialTypeMetadataContext;
import org.keycloak.examples.authenticator.credential.SecretQuestionCredentialModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class SecretQuestionCredentialProvider implements CredentialProvider<SecretQuestionCredentialModel>, CredentialInputValidator {
private static final Logger logger = Logger.getLogger(SecretQuestionCredentialProvider.class);
protected KeycloakSession session;
public SecretQuestionCredentialProvider(KeycloakSession session) {
this.session = session;
}
@Override
public boolean isValid(RealmModel realm, UserModel user, CredentialInput input) {
if (!(input instanceof UserCredentialModel)) {
logger.debug("Expected instance of UserCredentialModel for CredentialInput");
return false;
}
if (!input.getType().equals(getType())) {
return false;
}
String challengeResponse = input.getChallengeResponse();
if (challengeResponse == null) {
return false;
}
CredentialModel credentialModel = user.credentialManager().getStoredCredentialById(input.getCredentialId());
SecretQuestionCredentialModel sqcm = getCredentialFromModel(credentialModel);
return sqcm.getSecretQuestionSecretData().getAnswer().equals(challengeResponse);
}
@Override
public boolean supportsCredentialType(String credentialType) {
return getType().equals(credentialType);
}
@Override
public boolean isConfiguredFor(RealmModel realm, UserModel user, String credentialType) {
if (!supportsCredentialType(credentialType)) return false;
return user.credentialManager().getStoredCredentialsByTypeStream(credentialType).findAny().isPresent();
}
@Override
public CredentialModel createCredential(RealmModel realm, UserModel user, SecretQuestionCredentialModel credentialModel) {
if (credentialModel.getCreatedDate() == null) {
credentialModel.setCreatedDate(Time.currentTimeMillis());
}
return user.credentialManager().createStoredCredential(credentialModel);
}
@Override
public boolean deleteCredential(RealmModel realm, UserModel user, String credentialId) {
return user.credentialManager().removeStoredCredentialById(credentialId);
}
@Override
public SecretQuestionCredentialModel getCredentialFromModel(CredentialModel model) {
return SecretQuestionCredentialModel.createFromCredentialModel(model);
}
@Override
public CredentialTypeMetadata getCredentialTypeMetadata(CredentialTypeMetadataContext metadataContext) {
return CredentialTypeMetadata.builder()
.type(getType())
.category(CredentialTypeMetadata.Category.TWO_FACTOR)
.displayName(SecretQuestionCredentialProviderFactory.PROVIDER_ID)
.helpText("secret-question-text")
.createAction(SecretQuestionAuthenticatorFactory.PROVIDER_ID)
.removeable(false)
.build(session);
}
@Override
public String getType() {
return SecretQuestionCredentialModel.TYPE;
}
}

View file

@ -1,40 +0,0 @@
/*
* 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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class SecretQuestionCredentialProviderFactory implements CredentialProviderFactory<SecretQuestionCredentialProvider> {
public static final String PROVIDER_ID = "secret-question";
@Override
public String getId() {
return PROVIDER_ID;
}
@Override
public CredentialProvider create(KeycloakSession session) {
return new SecretQuestionCredentialProvider(session);
}
}

View file

@ -1,59 +0,0 @@
/*
* 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.CredentialRegistrator;
import org.keycloak.authentication.RequiredActionContext;
import org.keycloak.authentication.RequiredActionProvider;
import org.keycloak.credential.CredentialProvider;
import org.keycloak.examples.authenticator.credential.SecretQuestionCredentialModel;
import jakarta.ws.rs.core.Response;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class SecretQuestionRequiredAction implements RequiredActionProvider, CredentialRegistrator {
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"));
SecretQuestionCredentialProvider sqcp = (SecretQuestionCredentialProvider) context.getSession().getProvider(CredentialProvider.class, "secret-question");
sqcp.createCredential(context.getRealm(), context.getUser(), SecretQuestionCredentialModel.createSecretQuestion("What is your mom's first name?", answer));
context.success();
}
@Override
public void close() {
}
}

View file

@ -1,65 +0,0 @@
/*
* 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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @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() {
}
}

View file

@ -1,91 +0,0 @@
/*
* 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.credential;
import org.keycloak.common.util.Time;
import org.keycloak.credential.CredentialModel;
import org.keycloak.examples.authenticator.credential.dto.SecretQuestionCredentialData;
import org.keycloak.examples.authenticator.credential.dto.SecretQuestionSecretData;
import org.keycloak.util.JsonSerialization;
import java.io.IOException;
/**
* @author <a href="mailto:alistair.doswald@elca.ch">Alistair Doswald</a>
* @version $Revision: 1 $
*/
public class SecretQuestionCredentialModel extends CredentialModel {
public static final String TYPE = "SECRET_QUESTION";
private final SecretQuestionCredentialData credentialData;
private final SecretQuestionSecretData secretData;
private SecretQuestionCredentialModel(SecretQuestionCredentialData credentialData, SecretQuestionSecretData secretData) {
this.credentialData = credentialData;
this.secretData = secretData;
}
private SecretQuestionCredentialModel(String question, String answer) {
credentialData = new SecretQuestionCredentialData(question);
secretData = new SecretQuestionSecretData(answer);
}
public static SecretQuestionCredentialModel createSecretQuestion(String question, String answer) {
SecretQuestionCredentialModel credentialModel = new SecretQuestionCredentialModel(question, answer);
credentialModel.fillCredentialModelFields();
return credentialModel;
}
public static SecretQuestionCredentialModel createFromCredentialModel(CredentialModel credentialModel){
try {
SecretQuestionCredentialData credentialData = JsonSerialization.readValue(credentialModel.getCredentialData(), SecretQuestionCredentialData.class);
SecretQuestionSecretData secretData = JsonSerialization.readValue(credentialModel.getSecretData(), SecretQuestionSecretData.class);
SecretQuestionCredentialModel secretQuestionCredentialModel = new SecretQuestionCredentialModel(credentialData, secretData);
secretQuestionCredentialModel.setUserLabel(credentialModel.getUserLabel());
secretQuestionCredentialModel.setCreatedDate(credentialModel.getCreatedDate());
secretQuestionCredentialModel.setType(TYPE);
secretQuestionCredentialModel.setId(credentialModel.getId());
secretQuestionCredentialModel.setSecretData(credentialModel.getSecretData());
secretQuestionCredentialModel.setCredentialData(credentialModel.getCredentialData());
return secretQuestionCredentialModel;
} catch (IOException e){
throw new RuntimeException(e);
}
}
public SecretQuestionCredentialData getSecretQuestionCredentialData() {
return credentialData;
}
public SecretQuestionSecretData getSecretQuestionSecretData() {
return secretData;
}
private void fillCredentialModelFields(){
try {
setCredentialData(JsonSerialization.writeValueAsString(credentialData));
setSecretData(JsonSerialization.writeValueAsString(secretData));
setType(TYPE);
setCreatedDate(Time.currentTimeMillis());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

View file

@ -1,39 +0,0 @@
/*
* 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.credential.dto;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* @author <a href="mailto:alistair.doswald@elca.ch">Alistair Doswald</a>
* @version $Revision: 1 $
*/
public class SecretQuestionCredentialData {
private final String question;
@JsonCreator
public SecretQuestionCredentialData(@JsonProperty("question") String question) {
this.question = question;
}
public String getQuestion() {
return question;
}
}

View file

@ -1,39 +0,0 @@
/*
* 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.credential.dto;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* @author <a href="mailto:alistair.doswald@elca.ch">Alistair Doswald</a>
* @version $Revision: 1 $
*/
public class SecretQuestionSecretData {
private final String answer;
@JsonCreator
public SecretQuestionSecretData(@JsonProperty("answer") String answer) {
this.answer = answer;
}
public String getAnswer() {
return answer;
}
}

View file

@ -1,18 +0,0 @@
#
# 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

View file

@ -1,18 +0,0 @@
#
# 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

View file

@ -1 +0,0 @@
org.keycloak.examples.authenticator.SecretQuestionCredentialProviderFactory

View file

@ -32,6 +32,5 @@
<modules>
<module>rest</module>
<module>authenticator</module>
</modules>
</project>