diff --git a/.github/workflows/mavenpublish.yml b/.github/workflows/mavenpublish.yml
new file mode 100644
index 00000000..7d3a9f7e
--- /dev/null
+++ b/.github/workflows/mavenpublish.yml
@@ -0,0 +1,32 @@
+name: Maven Package
+
+on:
+ release:
+ types: [released]
+ push:
+ branches: [ master ]
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Set up JDK 18
+ uses: actions/setup-java@v1
+ with:
+ java-version: 18
+ server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
+ settings-path: ${{ github.workspace }} # location for the settings.xml file
+
+ - name: Test
+ run: mvn clean test
+
+ - name: Build with Maven
+ run: mvn -B package --file pom.xml
+
+ - name: Publish to GitHub Packages Apache Maven
+ run: mvn deploy -s $GITHUB_WORKSPACE/settings.xml
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
diff --git a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/providers/spi/impl/DefaultPhoneVerificationCodeProvider.java b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/providers/spi/impl/DefaultPhoneVerificationCodeProvider.java
index 6d83a5d3..430f1571 100644
--- a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/providers/spi/impl/DefaultPhoneVerificationCodeProvider.java
+++ b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/providers/spi/impl/DefaultPhoneVerificationCodeProvider.java
@@ -7,6 +7,7 @@
import cc.coopersoft.keycloak.phone.credential.PhoneOtpCredentialProvider;
import cc.coopersoft.keycloak.phone.credential.PhoneOtpCredentialProviderFactory;
import cc.coopersoft.keycloak.phone.providers.constants.TokenCodeType;
+import cc.coopersoft.keycloak.phone.providers.exception.PhoneNumberInvalidException;
import cc.coopersoft.keycloak.phone.providers.jpa.TokenCode;
import cc.coopersoft.keycloak.phone.providers.representations.TokenCodeRepresentation;
import cc.coopersoft.keycloak.phone.providers.spi.PhoneVerificationCodeProvider;
@@ -29,6 +30,7 @@
import java.time.Instant;
import java.util.Date;
import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
public class DefaultPhoneVerificationCodeProvider implements PhoneVerificationCodeProvider {
@@ -54,10 +56,11 @@ private RealmModel getRealm() {
public TokenCodeRepresentation ongoingProcess(String phoneNumber, TokenCodeType tokenCodeType) {
try {
+ String resultPhoneNumber = Utils.canonicalizePhoneNumber(session, phoneNumber);
TokenCode entity = getEntityManager()
.createNamedQuery("ongoingProcess", TokenCode.class)
.setParameter("realmId", getRealm().getId())
- .setParameter("phoneNumber", phoneNumber)
+ .setParameter("phoneNumber", resultPhoneNumber)
.setParameter("now", new Date(), TemporalType.TIMESTAMP)
.setParameter("type", tokenCodeType.name())
.getSingleResult();
@@ -75,6 +78,9 @@ public TokenCodeRepresentation ongoingProcess(String phoneNumber, TokenCodeType
return tokenCodeRepresentation;
} catch (NoResultException e) {
return null;
+ } catch (PhoneNumberInvalidException e) {
+ logger.warn("Invalid number: "+phoneNumber);
+ throw new BadRequestException("Phone number is invalid");
}
}
@@ -199,7 +205,7 @@ public void tokenValidated(UserModel user, String phoneNumber, String tokenCodeI
}
})
.map(CredentialModel::getId)
- .toList()
+ .collect(Collectors.toList())
.forEach(id -> u.credentialManager().removeStoredCredentialById(id));
});
}
diff --git a/keycloak-sms-provider-twofactorapi/README.md b/keycloak-sms-provider-twofactorapi/README.md
new file mode 100644
index 00000000..8110ea1d
--- /dev/null
+++ b/keycloak-sms-provider-twofactorapi/README.md
@@ -0,0 +1,17 @@
+# Twilio SMS Sender Provider
+
+**Not verify in Quarkus 19.0.1**
+
+```sh
+cp target/providers/keycloak-phone-provider.jar ${KEYCLOAK_HOME}/providers/
+cp target/providers/keycloak-phone-provider.resources.jar ${KEYCLOAK_HOME}/providers/
+cp target/providers/keycloak-sms-provider-twilio.jar ${KEYCLOAK_HOME}/providers/
+
+
+${KEYCLOAK_HOME}/bin/kc.sh build
+
+${KEYCLOAK_HOME}/bin/kc.sh start --spi-phone-default-service=twilio \
+ --spi-message-sender-service-twilio-account=${account} \
+ --spi-message-sender-service-twilio-token=${token} \
+ --spi-message-sender-service-twilio-number=${servicePhoneNumber}
+```
diff --git a/keycloak-sms-provider-twofactorapi/pom.xml b/keycloak-sms-provider-twofactorapi/pom.xml
new file mode 100644
index 00000000..d9892278
--- /dev/null
+++ b/keycloak-sms-provider-twofactorapi/pom.xml
@@ -0,0 +1,81 @@
+
+
+ 4.0.0
+
+
+ cc.coopersoft
+ keycloak-phone-provider-parent
+ 2.3.4-snapshot
+
+
+ keycloak-sms-provider-twofactorapi
+
+
+
+ cc.coopersoft
+ keycloak-phone-provider
+ 2.3.4-snapshot
+ provided
+
+
+ com.twilio.sdk
+ twilio
+ 9.2.4
+
+
+
+ com.squareup.okhttp3
+ okhttp
+ 4.10.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
+
+
+
+
+
+
+
diff --git a/keycloak-sms-provider-twofactorapi/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/TwoFactorMessageSenderServiceProviderFactory.java b/keycloak-sms-provider-twofactorapi/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/TwoFactorMessageSenderServiceProviderFactory.java
new file mode 100644
index 00000000..52b085dd
--- /dev/null
+++ b/keycloak-sms-provider-twofactorapi/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/TwoFactorMessageSenderServiceProviderFactory.java
@@ -0,0 +1,35 @@
+package cc.coopersoft.keycloak.phone.providers.sender;
+
+import cc.coopersoft.keycloak.phone.providers.spi.MessageSenderService;
+import cc.coopersoft.keycloak.phone.providers.spi.MessageSenderServiceProviderFactory;
+import org.keycloak.Config.Scope;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+
+public class TwoFactorMessageSenderServiceProviderFactory implements MessageSenderServiceProviderFactory {
+
+ private Scope config;
+
+ @Override
+ public MessageSenderService create(KeycloakSession session) {
+ return new TwoFactorSmsSenderServiceProvider(config,session.getContext().getRealm().getDisplayName());
+ }
+
+ @Override
+ public void init(Scope config) {
+ this.config = config;
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory keycloakSessionFactory) {
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public String getId() {
+ return "two-factor";
+ }
+}
diff --git a/keycloak-sms-provider-twofactorapi/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/TwoFactorSmsSenderServiceProvider.java b/keycloak-sms-provider-twofactorapi/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/TwoFactorSmsSenderServiceProvider.java
new file mode 100644
index 00000000..713686cc
--- /dev/null
+++ b/keycloak-sms-provider-twofactorapi/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/TwoFactorSmsSenderServiceProvider.java
@@ -0,0 +1,60 @@
+package cc.coopersoft.keycloak.phone.providers.sender;
+
+import cc.coopersoft.keycloak.phone.providers.exception.MessageSendException;
+import cc.coopersoft.keycloak.phone.providers.spi.FullSmsSenderAbstractService;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import org.jboss.logging.Logger;
+import org.keycloak.Config.Scope;
+
+import javax.annotation.PostConstruct;
+
+public class TwoFactorSmsSenderServiceProvider extends FullSmsSenderAbstractService {
+
+ private static final Logger logger = Logger.getLogger(TwoFactorSmsSenderServiceProvider.class);
+ private String twoFactorApiKey;
+ private static final String twoFactorUrl = "https://2factor.in/API/V1/";
+ private OkHttpClient client;
+
+ @PostConstruct
+ public void doSetUp() {
+ client = new OkHttpClient().newBuilder()
+ .build();
+ }
+
+ TwoFactorSmsSenderServiceProvider(Scope config, String realmDisplay) {
+ super(realmDisplay);
+ this.twoFactorApiKey = config.get("twoFactorApiKey");
+
+ }
+
+ @Override
+ public void sendMessage(String phoneNumber, String message) throws MessageSendException {
+
+ Request request = new Request.Builder()
+ .url(twoFactorUrl + twoFactorApiKey + "/SMS/" + phoneNumber + "/AUTOGEN/OTP1")
+ .get()
+ .build();
+ try (Response response = client.newCall(request).execute()) {
+ String responseString = response.body().string();
+ if (response.isSuccessful()) {
+ logger.info(responseString + ": sms sent successfully");
+ } else {
+ logger.error(responseString + ": sms sending failed");
+ throw new MessageSendException(response.code(),
+ String.valueOf(response.code()),
+ response.message());
+ }
+ } catch (Exception e) {
+ logger.error(e.getMessage());
+ throw new MessageSendException(400,
+ String.valueOf(400),
+ e.getMessage());
+ }
+ }
+
+ @Override
+ public void close() {
+ }
+}
diff --git a/keycloak-sms-provider-twofactorapi/src/main/resources/META-INF/services/cc.coopersoft.keycloak.phone.providers.spi.MessageSenderServiceProviderFactory b/keycloak-sms-provider-twofactorapi/src/main/resources/META-INF/services/cc.coopersoft.keycloak.phone.providers.spi.MessageSenderServiceProviderFactory
new file mode 100644
index 00000000..a0d83f36
--- /dev/null
+++ b/keycloak-sms-provider-twofactorapi/src/main/resources/META-INF/services/cc.coopersoft.keycloak.phone.providers.spi.MessageSenderServiceProviderFactory
@@ -0,0 +1 @@
+cc.coopersoft.keycloak.phone.providers.sender.TwilioMessageSenderServiceProviderFactory
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 956d9b48..64be29f9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -49,6 +49,15 @@
21.0.2
+
+
+
+ github
+ GitHub OWNER Apache Maven Packages
+ https://maven.pkg.github.com/cooperlyt/keycloak-phone-provider
+
+
+
keycloak-phone-provider
keycloak-phone-provider.resources
@@ -61,7 +70,7 @@
keycloak-sms-provider-yunxin
keycloak-sms-provider-aliyun
keycloak-sms-provider-tencent
-
+ keycloak-sms-provider-twofactorapi