diff --git a/README.md b/README.md
index 340b40ea..85ada52a 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Keycloak (Quarkus 21.x.x) Phone Provider
+# Keycloak (Quarkus 25.x.x) Phone Provider - Bate
![Build Status](https://github.com/cooperlyt/keycloak-phone-provider/actions/workflows/compile-and-liveness-check.yml/badge.svg)
![ci](https://github.com/cooperlyt/keycloak-phone-provider/actions/workflows/ci-keycloak20.yml/badge.svg)
![ci](https://github.com/cooperlyt/keycloak-phone-provider/actions/workflows/ci-keycloak21.yml/badge.svg)
@@ -23,6 +23,7 @@ Currently, there are implementations for:
+ TotalVoice
+ Twilio,
+ YunTongXun SMS
++ WeiXin App grant
More services can be added with ease due to the modularity of the code. In fact, nothing would stop you from implementing a
sender of TTS calls or WhatsApp messages.
@@ -276,6 +277,20 @@ Set Bind `Reset credentials with phone` to `Reset credentials flow`
+ `Update Phone Number` update user's phone number on next login.
+ `Configure OTP over SMS` update OTP Credential's phone number on next login.
+## ** WeiXin APP Grant
+
+Under `Authentication` > `Flows`:
++ Copy the `Direct Grant` flow to `Direct grant weixin app ` flow
++ Click on `Actions` > `Add step` on the `WX APP auth` line and move to first
++ Delete or disable other
+
+Under `Clients` > `$YOUR_CLIENT` > `Advanced` > `Authentication Flow Overrides`
+Set Direct Grant Flow to `Direct grant weixin app`
+
+
++ `POST /realms/{realmName}/protocol/openid-connect/token`
+ `Content-Type: application/x-www-form-urlencoded`
+ `grant_type=password&code=$CODE&client_id=$CLIENT_ID&client_secret=$CLIENT_SECRECT`
**Phone one key login**
diff --git a/examples/docker-compose.yml b/examples/docker-compose.yml
index f679c2af..574ad170 100644
--- a/examples/docker-compose.yml
+++ b/examples/docker-compose.yml
@@ -21,7 +21,7 @@ services:
keycloak:
- image: coopersoft/keycloak:21.0.1_phone-2.2.2
+ image: coopersoft/keycloak:25.0.2_phone-2.4.1-snapshot
# restart: always
ports:
- 8080:8080
diff --git a/keycloak-phone-provider.resources/pom.xml b/keycloak-phone-provider.resources/pom.xml
index be438464..a0ba9bba 100644
--- a/keycloak-phone-provider.resources/pom.xml
+++ b/keycloak-phone-provider.resources/pom.xml
@@ -7,7 +7,7 @@
cc.coopersoft
keycloak-phone-provider-parent
- 2.3.4-snapshot
+ 2.4.1-snapshot
keycloak-phone-provider.resources
diff --git a/keycloak-phone-provider/pom.xml b/keycloak-phone-provider/pom.xml
index 2c150073..b6f09b17 100644
--- a/keycloak-phone-provider/pom.xml
+++ b/keycloak-phone-provider/pom.xml
@@ -7,7 +7,7 @@
cc.coopersoft
keycloak-phone-provider-parent
- 2.3.4-snapshot
+ 2.4.1-snapshot
keycloak-phone-provider
diff --git a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/directgrant/AuthenticationCodeAuthenticatorFactory.java b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/directgrant/AuthenticationCodeAuthenticatorFactory.java
index 4669c6da..08d78450 100644
--- a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/directgrant/AuthenticationCodeAuthenticatorFactory.java
+++ b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/directgrant/AuthenticationCodeAuthenticatorFactory.java
@@ -39,7 +39,7 @@ public Authenticator create(KeycloakSession session) {
return new AuthenticationCodeAuthenticator(session);
}
- private static AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
+ private static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
AuthenticationExecutionModel.Requirement.REQUIRED,
// AuthenticationExecutionModel.Requirement.DISABLED
};
diff --git a/keycloak-sms-provider-aliyun/pom.xml b/keycloak-sms-provider-aliyun/pom.xml
index 8aa90ebe..3dd669e3 100644
--- a/keycloak-sms-provider-aliyun/pom.xml
+++ b/keycloak-sms-provider-aliyun/pom.xml
@@ -5,7 +5,7 @@
keycloak-phone-provider-parent
cc.coopersoft
- 2.3.4-snapshot
+ 2.4.1-snapshot
4.0.0
@@ -16,7 +16,7 @@
cc.coopersoft
keycloak-phone-provider
- 2.3.4-snapshot
+ 2.4.1-snapshot
provided
diff --git a/keycloak-sms-provider-aws-sns/pom.xml b/keycloak-sms-provider-aws-sns/pom.xml
index e165230e..0fcdedca 100755
--- a/keycloak-sms-provider-aws-sns/pom.xml
+++ b/keycloak-sms-provider-aws-sns/pom.xml
@@ -7,7 +7,7 @@
cc.coopersoft
keycloak-phone-provider-parent
- 2.3.4-snapshot
+ 2.4.1-snapshot
keycloak-sms-provider-aws-sns
@@ -16,7 +16,7 @@
cc.coopersoft
keycloak-phone-provider
- 2.3.4-snapshot
+ 2.4.1-snapshot
provided
diff --git a/keycloak-sms-provider-bulksms/pom.xml b/keycloak-sms-provider-bulksms/pom.xml
index 4ad6ce18..1059781c 100644
--- a/keycloak-sms-provider-bulksms/pom.xml
+++ b/keycloak-sms-provider-bulksms/pom.xml
@@ -7,7 +7,7 @@
cc.coopersoft
keycloak-phone-provider-parent
- 2.3.4-snapshot
+ 2.4.1-snapshot
keycloak-sms-provider-bulksms
@@ -16,7 +16,7 @@
cc.coopersoft
keycloak-phone-provider
- 2.3.4-snapshot
+ 2.4.1-snapshot
provided
diff --git a/keycloak-sms-provider-cloopen/pom.xml b/keycloak-sms-provider-cloopen/pom.xml
index ebacb560..e2c12d3d 100644
--- a/keycloak-sms-provider-cloopen/pom.xml
+++ b/keycloak-sms-provider-cloopen/pom.xml
@@ -5,7 +5,7 @@
cc.coopersoft
keycloak-phone-provider-parent
- 2.3.4-snapshot
+ 2.4.1-snapshot
4.0.0
@@ -15,7 +15,7 @@
cc.coopersoft
keycloak-phone-provider
- 2.3.4-snapshot
+ 2.4.1-snapshot
provided
diff --git a/keycloak-sms-provider-dummy/pom.xml b/keycloak-sms-provider-dummy/pom.xml
index 455bc3d7..c7dc4f8d 100644
--- a/keycloak-sms-provider-dummy/pom.xml
+++ b/keycloak-sms-provider-dummy/pom.xml
@@ -7,7 +7,7 @@
cc.coopersoft
keycloak-phone-provider-parent
- 2.3.4-snapshot
+ 2.4.1-snapshot
keycloak-sms-provider-dummy
@@ -16,7 +16,7 @@
cc.coopersoft
keycloak-phone-provider
- 2.3.4-snapshot
+ 2.4.1-snapshot
provided
diff --git a/keycloak-sms-provider-tencent/pom.xml b/keycloak-sms-provider-tencent/pom.xml
index 45481aa7..aa23162b 100644
--- a/keycloak-sms-provider-tencent/pom.xml
+++ b/keycloak-sms-provider-tencent/pom.xml
@@ -5,7 +5,7 @@
keycloak-phone-provider-parent
cc.coopersoft
- 2.3.4-snapshot
+ 2.4.1-snapshot
4.0.0
@@ -16,7 +16,7 @@
cc.coopersoft
keycloak-phone-provider
- 2.3.4-snapshot
+ 2.4.1-snapshot
provided
diff --git a/keycloak-sms-provider-totalvoice/pom.xml b/keycloak-sms-provider-totalvoice/pom.xml
index 84eff5c5..55dad008 100644
--- a/keycloak-sms-provider-totalvoice/pom.xml
+++ b/keycloak-sms-provider-totalvoice/pom.xml
@@ -7,7 +7,7 @@
cc.coopersoft
keycloak-phone-provider-parent
- 2.3.4-snapshot
+ 2.4.1-snapshot
keycloak-sms-provider-totalvoice
@@ -16,7 +16,7 @@
cc.coopersoft
keycloak-phone-provider
- 2.3.4-snapshot
+ 2.4.1-snapshot
provided
diff --git a/keycloak-sms-provider-twilio/pom.xml b/keycloak-sms-provider-twilio/pom.xml
index e5575590..645787c1 100644
--- a/keycloak-sms-provider-twilio/pom.xml
+++ b/keycloak-sms-provider-twilio/pom.xml
@@ -7,7 +7,7 @@
cc.coopersoft
keycloak-phone-provider-parent
- 2.3.4-snapshot
+ 2.4.1-snapshot
keycloak-sms-provider-twilio
@@ -16,7 +16,7 @@
cc.coopersoft
keycloak-phone-provider
- 2.3.4-snapshot
+ 2.4.1-snapshot
provided
diff --git a/keycloak-sms-provider-twofactorapi/pom.xml b/keycloak-sms-provider-twofactorapi/pom.xml
index 52e3d292..66b23dea 100644
--- a/keycloak-sms-provider-twofactorapi/pom.xml
+++ b/keycloak-sms-provider-twofactorapi/pom.xml
@@ -7,7 +7,7 @@
cc.coopersoft
keycloak-phone-provider-parent
- 2.3.4-snapshot
+ 2.4.1-snapshot
keycloak-sms-provider-twofactorapi
@@ -16,7 +16,7 @@
cc.coopersoft
keycloak-phone-provider
- 2.3.4-snapshot
+ 2.4.1-snapshot
provided
diff --git a/keycloak-sms-provider-yunxin/pom.xml b/keycloak-sms-provider-yunxin/pom.xml
index a3a20d4d..176d995c 100644
--- a/keycloak-sms-provider-yunxin/pom.xml
+++ b/keycloak-sms-provider-yunxin/pom.xml
@@ -5,7 +5,7 @@
keycloak-phone-provider-parent
cc.coopersoft
- 2.3.4-snapshot
+ 2.4.1-snapshot
4.0.0
@@ -15,7 +15,7 @@
cc.coopersoft
keycloak-phone-provider
- 2.3.4-snapshot
+ 2.4.1-snapshot
provided
diff --git a/keycloak-wx-provider-app/pom.xml b/keycloak-wx-provider-app/pom.xml
new file mode 100644
index 00000000..a2f64258
--- /dev/null
+++ b/keycloak-wx-provider-app/pom.xml
@@ -0,0 +1,75 @@
+
+
+ 4.0.0
+
+ cc.coopersoft
+ keycloak-phone-provider-parent
+ 2.4.1-snapshot
+
+
+ keycloak-wx-provider-app
+
+
+ 21
+ 21
+ UTF-8
+
+
+
+
+ com.squareup.okhttp3
+ okhttp
+ 4.12.0
+
+
+
+
+
+
+ maven-assembly-plugin
+
+
+ package
+
+ single
+
+
+
+
+
+ jar-with-dependencies
+
+ ${project.build.finalName}
+ false
+
+
+
+ maven-dependency-plugin
+
+
+ package
+
+ copy
+
+
+
+
+ ${project.groupId}
+ ${project.artifactId}
+ ${project.version}
+
+
+ ../target/providers
+ true
+ true
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/keycloak-wx-provider-app/src/main/java/cc/coopersoft/keycloak/wx/app/providers/directgrant/WXAppAuthenticator.java b/keycloak-wx-provider-app/src/main/java/cc/coopersoft/keycloak/wx/app/providers/directgrant/WXAppAuthenticator.java
new file mode 100644
index 00000000..3284f07f
--- /dev/null
+++ b/keycloak-wx-provider-app/src/main/java/cc/coopersoft/keycloak/wx/app/providers/directgrant/WXAppAuthenticator.java
@@ -0,0 +1,211 @@
+package cc.coopersoft.keycloak.wx.app.providers.directgrant;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import org.jboss.logging.Logger;
+import org.keycloak.authentication.AuthenticationFlowContext;
+import org.keycloak.authentication.AuthenticationFlowError;
+import org.keycloak.authentication.Authenticator;
+import org.keycloak.events.Errors;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.representations.idm.OAuth2ErrorRepresentation;
+import org.keycloak.utils.StringUtil;
+
+import java.io.IOException;
+import java.util.Optional;
+
+public class WXAppAuthenticator implements Authenticator {
+
+ private static final Logger logger = Logger.getLogger(WXAppAuthenticator.class);
+
+ private static final String CODE_PARAM_NAME = "code";
+
+ private static final String WX_CODE_TO_SESSION_URI = "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code";
+
+ private static final String USER_UNION_ID_ATTRIBUTE = "wx_union_id";
+
+ private static final String USER_OPEN_ID_ATTRIBUTE = "wx_open_id";
+
+ @Override
+ public void authenticate(AuthenticationFlowContext context) {
+ context.clearUser();
+ getCode(context).flatMap(code -> getWXUserSession(context,code)).ifPresent(session ->
+ validUser(context, session.unionid, session.openid));
+ }
+
+ @Override
+ public void action(AuthenticationFlowContext authenticationFlowContext) {
+ authenticate(authenticationFlowContext);
+ }
+
+ @Override
+ public boolean requiresUser() {
+ return false;
+ }
+
+ @Override
+ public boolean configuredFor(KeycloakSession keycloakSession, RealmModel realmModel, UserModel userModel) {
+ return true;
+ }
+
+ @Override
+ public void setRequiredActions(KeycloakSession keycloakSession, RealmModel realmModel, UserModel userModel) {
+ }
+
+ @Override
+ public void close() {
+ }
+
+ private Response errorResponse(int status, String error, String errorDescription) {
+ OAuth2ErrorRepresentation errorRep = new OAuth2ErrorRepresentation(error, errorDescription);
+ return Response.status(status).entity(errorRep).type(MediaType.APPLICATION_JSON_TYPE).build();
+ }
+
+ private Optional getCode(AuthenticationFlowContext context) {
+ String code = context.getHttpRequest().getDecodedFormParameters().getFirst(CODE_PARAM_NAME);
+ if (StringUtil.isNotBlank(code)){
+ return Optional.of(code);
+ }
+ context.getEvent().error(Errors.INVALID_REQUEST);
+ Response challenge = errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), "invalid_request",
+ "Invalid WX APP grant request, code is must");
+ context.failure(AuthenticationFlowError.INVALID_CREDENTIALS, challenge);
+ return Optional.empty();
+ }
+
+ private Optional getWXUserSession(AuthenticationFlowContext context, String code){
+ logger.info("WX APP grant request by:" + code);
+
+ return getWXAPICredentials(context).flatMap(cer -> {
+ OkHttpClient okHttpClient = new OkHttpClient();
+
+ Request request = new Request.Builder()
+ .addHeader("content-type", "application/json")
+ .url(String.format(WX_CODE_TO_SESSION_URI, cer.id, cer.secret, code))
+ .get()
+ .build();
+
+
+ try (okhttp3.Response response = okHttpClient.newCall(request).execute()){
+ if (response.isSuccessful() && response.body() != null){
+ ObjectMapper objectMapper = new ObjectMapper();
+ WXUserSessionInfo result = objectMapper.readValue(response.body().string(), WXUserSessionInfo.class);
+ logger.debug("wx api result: " + result);
+ if (result.valid()){
+ return Optional.of(result);
+ }
+
+ logger.error("WX API response invalid " + result.errcode + ":" + result.errmsg);
+ context.getEvent().error(Errors.INVALID_REQUEST);
+ Response challenge = errorResponse(Response.Status.BAD_GATEWAY.getStatusCode(), "invalid_wx_api_exception",
+ "WX API call invalid" + result.errcode + ":" + result.errmsg);
+ context.failure(AuthenticationFlowError.ACCESS_DENIED, challenge);
+ return Optional.empty();
+ }
+
+ logger.error("WX API call exception, code:" + response.code());
+ context.getEvent().error(Errors.INVALID_REQUEST);
+ Response challenge = errorResponse(Response.Status.BAD_GATEWAY.getStatusCode(), "invalid_wx_api_exception",
+ "WX API call exception, code:" + response.code());
+ context.failure(AuthenticationFlowError.ACCESS_DENIED, challenge);
+ return Optional.empty();
+
+ } catch (IOException e) {
+ logger.error(e.getMessage(),e);
+ context.getEvent().error(Errors.INVALID_REQUEST);
+ Response challenge = errorResponse(Response.Status.BAD_GATEWAY.getStatusCode(), "invalid_wx_api_exception",
+ "WX API call exception");
+ context.failure(AuthenticationFlowError.ACCESS_DENIED, challenge);
+ return Optional.empty();
+ }
+ });
+ }
+
+ private boolean isAllowEvery(AuthenticationFlowContext context) {
+ return context.getAuthenticatorConfig() == null ||
+ context.getAuthenticatorConfig().getConfig().getOrDefault(WXAppAuthenticatorFactory.CONFIG_EVERY, "true")
+ .equals("true");
+ }
+
+ private Optional getWXAPICredentials(AuthenticationFlowContext context){
+
+ String appid = context.getAuthenticatorConfig().getConfig().get(WXAppAuthenticatorFactory.WX_API_ID);
+ String secret = context.getAuthenticatorConfig().getConfig().get(WXAppAuthenticatorFactory.WX_API_SECRET);
+
+ if (StringUtil.isNotBlank(appid) && StringUtil.isNotBlank(secret)){
+ return Optional.of(new WXAPICredentials(appid,secret));
+ }
+
+ context.getEvent().error(Errors.INVALID_REQUEST);
+ Response challenge = errorResponse(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), "invalid_api_credentials",
+ "WX API credentials not config");
+ context.failure(AuthenticationFlowError.ACCESS_DENIED, challenge);
+ return Optional.empty();
+
+ }
+
+ private Optional findUser(AuthenticationFlowContext context, String unionId, String openId){
+
+ var userProvider = context.getSession().users();
+
+ return userProvider.searchForUserByUserAttributeStream(context.getRealm(), USER_UNION_ID_ATTRIBUTE, unionId).findFirst()
+ .or(() -> userProvider.searchForUserByUserAttributeStream(context.getRealm(), USER_OPEN_ID_ATTRIBUTE, openId).findFirst())
+ .or(() -> createEveryUser(context,unionId,openId));
+
+ }
+
+ private Optional createEveryUser(AuthenticationFlowContext context, String unionId, String openId){
+ if (isAllowEvery(context)){
+ UserModel newUser = context.getSession().users().addUser(context.getRealm(), unionId);
+
+ newUser.setEnabled(true);
+ newUser.setSingleAttribute(USER_UNION_ID_ATTRIBUTE, unionId);
+ newUser.setSingleAttribute(USER_OPEN_ID_ATTRIBUTE, openId);
+ //context.getAuthenticationSession().setClientNote(OIDCLoginProtocol.LOGIN_HINT_PARAM, unionId);
+ logger.info("create user by wx :" + unionId);
+ return Optional.of(newUser);
+
+ }
+ context.getEvent().error(Errors.USER_NOT_FOUND);
+ Response challenge = errorResponse(Response.Status.FORBIDDEN.getStatusCode(), "invalid_user",
+ "Invalid WX User:" + unionId);
+ context.failure(AuthenticationFlowError.INVALID_CREDENTIALS, challenge);
+ return Optional.empty();
+ }
+
+ private void validUser(AuthenticationFlowContext context, String unionId, String openId){
+ findUser(context,unionId,openId).ifPresent(
+ user -> {
+ context.setUser(user);
+ context.success();
+ }
+ );
+ }
+
+
+ @Data
+ @NoArgsConstructor
+ private static class WXUserSessionInfo {
+ private String sessionKey; // 会话密钥
+ private String unionid;// 用户在开放平台的唯一标识符,若当前小程序已绑定到微信开放平台账号下会返回,详见 UnionID 机制说明。
+ private String errmsg; // 错误信息
+ private String openid;// 用户唯一标识
+ private int errcode;// int32 错误码
+
+ public boolean valid(){
+ return StringUtil.isNotBlank(unionid) && StringUtil.isNotBlank(openid);
+ }
+
+ }
+
+ private record WXAPICredentials(String id, String secret) {
+
+ }
+}
diff --git a/keycloak-wx-provider-app/src/main/java/cc/coopersoft/keycloak/wx/app/providers/directgrant/WXAppAuthenticatorFactory.java b/keycloak-wx-provider-app/src/main/java/cc/coopersoft/keycloak/wx/app/providers/directgrant/WXAppAuthenticatorFactory.java
new file mode 100644
index 00000000..8b2387ec
--- /dev/null
+++ b/keycloak-wx-provider-app/src/main/java/cc/coopersoft/keycloak/wx/app/providers/directgrant/WXAppAuthenticatorFactory.java
@@ -0,0 +1,115 @@
+package cc.coopersoft.keycloak.wx.app.providers.directgrant;
+
+import org.keycloak.Config;
+import org.keycloak.authentication.Authenticator;
+import org.keycloak.authentication.AuthenticatorFactory;
+import org.keycloak.models.AuthenticationExecutionModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.provider.ProviderConfigProperty;
+import org.keycloak.provider.ProviderConfigurationBuilder;
+
+import java.util.Collections;
+import java.util.List;
+
+import static org.keycloak.provider.ProviderConfigProperty.BOOLEAN_TYPE;
+
+public class WXAppAuthenticatorFactory implements AuthenticatorFactory {
+
+
+
+ public static final String PROVIDER_ID = "wx-app-authenticator";
+
+ public static final String CONFIG_EVERY = "every";
+
+ public static final String WX_API_ID = "appid";
+
+ public static final String WX_API_SECRET = "app_secret";
+
+ private static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
+ AuthenticationExecutionModel.Requirement.REQUIRED,
+ };
+
+ private static final List CONFIG_PROPERTIES;
+
+ static {
+ CONFIG_PROPERTIES = ProviderConfigurationBuilder.create()
+ .property().name(CONFIG_EVERY)
+ .type(ProviderConfigProperty.BOOLEAN_TYPE)
+ .label("Every body")
+ .helpText("Auto register user.")
+ .defaultValue(true)
+ .add()
+ .property().name(WX_API_ID)
+ .type(ProviderConfigProperty.STRING_TYPE)
+ .label("WX API ID")
+ .helpText("WX API ID")
+ .add()
+ .property().name(WX_API_SECRET)
+ .type(ProviderConfigProperty.STRING_TYPE)
+ .label("WX API secret")
+ .helpText("WX API secret")
+ .add()
+ .build();
+ }
+
+ @Override
+ public String getDisplayType() {
+ return "WX APP auth";
+ }
+
+ @Override
+ public String getReferenceCategory() {
+ return "WX APP Grant";
+ }
+
+ @Override
+ public boolean isConfigurable() {
+ return true;
+ }
+
+ @Override
+ public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
+ return REQUIREMENT_CHOICES;
+ }
+
+ @Override
+ public boolean isUserSetupAllowed() {
+ return true;
+ }
+
+ @Override
+ public String getHelpText() {
+ return "WX APP auth";
+ }
+
+ @Override
+ public List getConfigProperties() {
+ return CONFIG_PROPERTIES;
+ }
+
+ @Override
+ public Authenticator create(KeycloakSession session) {
+ return new WXAppAuthenticator();
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+
+ }
+
+ @Override
+ public void close() {
+
+ }
+
+ @Override
+ public String getId() {
+ return PROVIDER_ID;
+ }
+}
diff --git a/keycloak-wx-provider-app/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory b/keycloak-wx-provider-app/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory
new file mode 100755
index 00000000..ad85ad30
--- /dev/null
+++ b/keycloak-wx-provider-app/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory
@@ -0,0 +1 @@
+cc.coopersoft.keycloak.wx.app.providers.directgrant.WXAppAuthenticatorFactory
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 7bca9934..f4dace82 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,7 +7,7 @@
cc.coopersoft
keycloak-phone-provider-parent
pom
- 2.3.4-snapshot
+ 2.4.1-snapshot
keycloak-phone-provider
keycloak phone support.
@@ -71,6 +71,7 @@
keycloak-sms-provider-aliyun
keycloak-sms-provider-tencent
keycloak-sms-provider-twofactorapi
+ keycloak-wx-provider-app