diff --git a/.github/workflows/compile-and-liveness-check.yml b/.github/workflows/compile-and-liveness-check.yml
index 7a79ed99..14eaec0a 100644
--- a/.github/workflows/compile-and-liveness-check.yml
+++ b/.github/workflows/compile-and-liveness-check.yml
@@ -10,7 +10,7 @@ jobs:
- uses: actions/setup-java@v3
with:
distribution: 'temurin'
- java-version: '17'
+ java-version: '21'
- shell: bash
run: cd examples && ./docker-build.sh test
- run: cd examples && docker compose --verbose up --build --wait
diff --git a/.github/workflows/mavenpublish.yml b/.github/workflows/mavenpublish.yml
index 7d3a9f7e..d4a6c3f8 100644
--- a/.github/workflows/mavenpublish.yml
+++ b/.github/workflows/mavenpublish.yml
@@ -13,10 +13,10 @@ jobs:
steps:
- uses: actions/checkout@v2
- - name: Set up JDK 18
+ - name: Set up JDK 21
uses: actions/setup-java@v1
with:
- java-version: 18
+ java-version: 21
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
settings-path: ${{ github.workspace }} # location for the settings.xml file
diff --git a/jboss-cli/module-add.cli b/jboss-cli/module-add.cli
index 9141136f..0a42d069 100644
--- a/jboss-cli/module-add.cli
+++ b/jboss-cli/module-add.cli
@@ -3,7 +3,7 @@
# main provider
module add --name=com.googlecode.libphonenumber --resources=libphonenumber-8.13.7.jar
-module add --name=keycloak-phone-provider --resources=keycloak-phone-provider.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-common,org.hibernate,org.keycloak.keycloak-server-spi,org.keycloak.keycloak-server-spi-private,org.keycloak.keycloak-services,org.keycloak.keycloak-model-jpa,org.jboss.logging,javax.api,javax.ws.rs.api,javax.transaction.api,javax.persistence.api,org.jboss.resteasy.resteasy-jaxrs,org.apache.httpcomponents,org.apache.commons.lang,javax.xml.bind.api,com.squareup.okhttp3,com.googlecode.libphonenumber
+module add --name=keycloak-phone-provider --resources=keycloak-phone-provider.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-common,org.hibernate,org.keycloak.keycloak-server-spi,org.keycloak.keycloak-server-spi-private,org.keycloak.keycloak-services,org.keycloak.keycloak-model-jpa,org.jboss.logging,javax.api,jakarta.ws.rs.api,javax.transaction.api,javax.persistence.api,org.jboss.resteasy.resteasy-jaxrs,org.apache.httpcomponents,org.apache.commons.lang,javax.xml.bind.api,com.squareup.okhttp3,com.googlecode.libphonenumber
# dummy provider
module add --name=keycloak-sms-provider-dummy --resources=keycloak-sms-provider-dummy.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi,org.jboss.logging,keycloak-phone-provider
diff --git a/keycloak-phone-provider/pom.xml b/keycloak-phone-provider/pom.xml
index 9cffc6da..2c150073 100644
--- a/keycloak-phone-provider/pom.xml
+++ b/keycloak-phone-provider/pom.xml
@@ -20,9 +20,24 @@
provided
- com.googlecode.libphonenumber
- libphonenumber
- 8.13.7
+ jakarta.validation
+ jakarta.validation-api
+ 3.1.0
+
+
+ com.googlecode.libphonenumber
+ libphonenumber
+ 8.13.41
+
+
+ com.squareup.okhttp3
+ okhttp
+ 4.12.0
+
+
+ org.jboss.resteasy
+ resteasy-jaxrs
+ 3.15.6.Final
diff --git a/keycloak-phone-provider/src/main/java/cc/coopersoft/common/OptionalUtils.java b/keycloak-phone-provider/src/main/java/cc/coopersoft/common/OptionalUtils.java
index 00ab66ba..fff81e69 100644
--- a/keycloak-phone-provider/src/main/java/cc/coopersoft/common/OptionalUtils.java
+++ b/keycloak-phone-provider/src/main/java/cc/coopersoft/common/OptionalUtils.java
@@ -3,19 +3,18 @@
import org.keycloak.services.validation.Validation;
import java.util.Optional;
-import java.util.regex.Pattern;
public class OptionalUtils {
- public static Optional ofEmpty(String str){
+ public static Optional ofEmpty(String str) {
return Validation.isEmpty(str) ? Optional.empty() : Optional.of(str);
}
- public static Optional ofBlank(String str){
+ public static Optional ofBlank(String str) {
return Validation.isBlank(str) ? Optional.empty() : Optional.of(str).map(String::trim);
}
- public static Optional ofTrue(boolean b){
+ public static Optional ofTrue(boolean b) {
return b ? Optional.of(true) : Optional.empty();
}
}
diff --git a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/Utils.java b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/Utils.java
index 017a64b9..5c075a78 100644
--- a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/Utils.java
+++ b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/Utils.java
@@ -1,23 +1,17 @@
package cc.coopersoft.keycloak.phone;
import cc.coopersoft.common.OptionalUtils;
-import cc.coopersoft.keycloak.phone.credential.PhoneOtpCredentialModel;
import cc.coopersoft.keycloak.phone.providers.exception.PhoneNumberInvalidException;
import cc.coopersoft.keycloak.phone.providers.spi.PhoneProvider;
import org.jboss.logging.Logger;
-import org.keycloak.credential.CredentialModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import com.google.i18n.phonenumbers.*;
import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat;
-import org.keycloak.models.credential.dto.OTPSecretData;
-import org.keycloak.services.validation.Validation;
-import org.keycloak.util.JsonSerialization;
-import javax.validation.constraints.NotNull;
-import java.io.IOException;
+import jakarta.validation.constraints.NotNull;
import java.util.*;
import java.util.function.Predicate;
import java.util.regex.Pattern;
@@ -25,119 +19,123 @@
public class Utils {
private static final Logger logger = Logger.getLogger(Utils.class);
- public static Optional findUserByPhone(KeycloakSession session,RealmModel realm, String phoneNumber){
+ public static Optional findUserByPhone(KeycloakSession session, RealmModel realm, String phoneNumber) {
var userProvider = session.users();
Set numbers = new HashSet<>();
numbers.add(phoneNumber);
- if (session.getProvider(PhoneProvider.class).compatibleMode()){
+ if (session.getProvider(PhoneProvider.class).compatibleMode()) {
var phoneNumberUtil = PhoneNumberUtil.getInstance();
try {
var parsedNumber = phoneNumberUtil.parse(phoneNumber, defaultRegion(session));
- if (parsedNumber.hasNationalNumber()){
- numbers.add(String.valueOf(parsedNumber.getNationalNumber())) ;
+ if (parsedNumber.hasNationalNumber()) {
+ numbers.add(String.valueOf(parsedNumber.getNationalNumber()));
}
for (PhoneNumberFormat format : PhoneNumberFormat.values()) {
numbers.add(phoneNumberUtil.format(parsedNumber, format));
}
- }catch (NumberParseException e){
- logger.warn(String.format("%s is not a valid phone number!",phoneNumber),e);
+ } catch (NumberParseException e) {
+ logger.warn(String.format("%s is not a valid phone number!", phoneNumber), e);
}
}
-
return numbers.stream().flatMap(number -> userProvider
- .searchForUserByUserAttributeStream(realm,"phoneNumber", number))
- .max((u1, u2) -> {
- var result = comparatorAttributesAnyMatch(u1,u2,"phoneNumberVerified","true"::equals);
- if (result == 0){
- result = comparatorAttributesAnyMatch(u1,u2,"phoneNumber", number -> number.startsWith("+"));
- }
- return result;
- });
+ .searchForUserByUserAttributeStream(realm, "phoneNumber", number))
+ .max((u1, u2) -> {
+ var result = comparatorAttributesAnyMatch(u1, u2, "phoneNumberVerified", "true"::equals);
+ if (result == 0) {
+ result = comparatorAttributesAnyMatch(u1, u2, "phoneNumber", number -> number.startsWith("+"));
+ }
+ return result;
+ });
}
-// public static Optional findUserByPhone(UserProvider userProvider, RealmModel realm, String phoneNumber, String notIs){
-// return userProvider
-// .searchForUserByUserAttributeStream(realm, "phoneNumber", phoneNumber)
-// .filter(u -> !u.getId().equals(notIs))
-// .max(comparatorUser());
-// }
+ // public static Optional findUserByPhone(UserProvider userProvider,
+ // RealmModel realm, String phoneNumber, String notIs){
+ // return userProvider
+ // .searchForUserByUserAttributeStream(realm, "phoneNumber", phoneNumber)
+ // .filter(u -> !u.getId().equals(notIs))
+ // .max(comparatorUser());
+ // }
private static int comparatorAttributesAnyMatch(UserModel user1, UserModel user2,
- String attribute, Predicate super String> predicate){
+ String attribute, Predicate super String> predicate) {
return Boolean.compare(user1.getAttributeStream(attribute).anyMatch(predicate),
user2.getAttributeStream(attribute).anyMatch(predicate));
}
- private static Optional localeToCountry(String locale){
+ private static Optional localeToCountry(String locale) {
return OptionalUtils.ofBlank(locale).flatMap(l -> {
Pattern countryRegx = Pattern.compile("[^a-z]*\\-?([A-Z]{2,3})");
return Optional.of(countryRegx.matcher(l))
- .flatMap(m -> m.find() ? OptionalUtils.ofBlank(m.group(1)) : Optional.empty());
+ .flatMap(m -> m.find() ? OptionalUtils.ofBlank(m.group(1)) : Optional.empty());
});
}
-
- private static String defaultRegion(KeycloakSession session){
+ private static String defaultRegion(KeycloakSession session) {
var defaultRegion = session.getProvider(PhoneProvider.class).defaultPhoneRegion();
- return defaultRegion.orElseGet(() -> localeToCountry(session.getContext().getRealm().getDefaultLocale()).orElse(null));
+ return defaultRegion
+ .orElseGet(() -> localeToCountry(session.getContext().getRealm().getDefaultLocale()).orElse(null));
}
/**
- * Parses a phone number with google's libphonenumber and then outputs it's
- * international canonical form
- *
- */
- public static String canonicalizePhoneNumber(KeycloakSession session,@NotNull String phoneNumber) throws PhoneNumberInvalidException {
+ * Parses a phone number with google's libphonenumber and then outputs it's
+ * international canonical form
+ *
+ */
+ public static String canonicalizePhoneNumber(KeycloakSession session, @NotNull String phoneNumber)
+ throws PhoneNumberInvalidException {
var provider = session.getProvider(PhoneProvider.class);
-
var phoneNumberUtil = PhoneNumberUtil.getInstance();
var resultPhoneNumber = phoneNumber.trim();
var defaultRegion = defaultRegion(session);
- logger.info(String.format("default region '%s' will be used",defaultRegion));
+ logger.info(String.format("default region '%s' will be used", defaultRegion));
try {
var parsedNumber = phoneNumberUtil.parse(resultPhoneNumber, defaultRegion);
if (provider.validPhoneNumber() && !phoneNumberUtil.isValidNumber(parsedNumber)) {
- logger.info(String.format("Phone number [%s] Valid fail with google's libphonenumber",resultPhoneNumber));
+ logger.info(
+ String.format("Phone number [%s] Valid fail with google's libphonenumber", resultPhoneNumber));
throw new PhoneNumberInvalidException(PhoneNumberInvalidException.ErrorType.VALID_FAIL,
- String.format("Phone number [%s] Valid fail with google's libphonenumber",resultPhoneNumber));
+ String.format("Phone number [%s] Valid fail with google's libphonenumber", resultPhoneNumber));
}
var canonicalizeFormat = provider.canonicalizePhoneNumber();
try {
resultPhoneNumber = canonicalizeFormat
- .map(PhoneNumberFormat::valueOf)
- .map(format -> phoneNumberUtil.format(parsedNumber, format))
- .orElse(resultPhoneNumber);
- }catch (RuntimeException e){
- logger.warn(String.format("canonicalize format param error! '%s' is not in supported list: %s, E164 Will be used.",
- Arrays.toString(PhoneNumberFormat.values()),
- canonicalizeFormat.orElse("")),e);
+ .map(PhoneNumberFormat::valueOf)
+ .map(format -> phoneNumberUtil.format(parsedNumber, format))
+ .orElse(resultPhoneNumber);
+ } catch (RuntimeException e) {
+ logger.warn(String.format(
+ "canonicalize format param error! '%s' is not in supported list: %s, E164 Will be used.",
+ Arrays.toString(PhoneNumberFormat.values()),
+ canonicalizeFormat.orElse("")), e);
resultPhoneNumber = phoneNumberUtil.format(parsedNumber, PhoneNumberFormat.E164);
}
var phoneNumberRegex = provider.phoneNumberRegex();
- if (!phoneNumberRegex.map(resultPhoneNumber::matches).orElse(true)){
- logger.info(String.format("Phone number [%s] not match regex '%s'",resultPhoneNumber, phoneNumberRegex.orElse("")));
+ if (!phoneNumberRegex.map(resultPhoneNumber::matches).orElse(true)) {
+ logger.info(String.format("Phone number [%s] not match regex '%s'", resultPhoneNumber,
+ phoneNumberRegex.orElse("")));
throw new PhoneNumberInvalidException(PhoneNumberInvalidException.ErrorType.NOT_SUPPORTED,
- String.format("Phone number [%s] not match regex '%s'",resultPhoneNumber, phoneNumberRegex.orElse("")));
+ String.format("Phone number [%s] not match regex '%s'", resultPhoneNumber,
+ phoneNumberRegex.orElse("")));
}
return resultPhoneNumber;
- }catch (NumberParseException e){
+ } catch (NumberParseException e) {
logger.info(e);
throw new PhoneNumberInvalidException(e);
}
}
- public static boolean isDuplicatePhoneAllowed(KeycloakSession session){
+ public static boolean isDuplicatePhoneAllowed(KeycloakSession session) {
return session.getProvider(PhoneProvider.class).isDuplicatePhoneAllowed();
}
- public static int getOtpExpires(KeycloakSession session){
+ public static int getOtpExpires(KeycloakSession session) {
return session.getProvider(PhoneProvider.class).otpExpires();
}
diff --git a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/browser/PhoneUsernamePasswordForm.java b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/browser/PhoneUsernamePasswordForm.java
index 2f3e2c99..7979d8b7 100644
--- a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/browser/PhoneUsernamePasswordForm.java
+++ b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/browser/PhoneUsernamePasswordForm.java
@@ -27,8 +27,8 @@
import org.keycloak.services.messages.Messages;
import org.keycloak.services.validation.Validation;
-import javax.ws.rs.core.MultivaluedMap;
-import javax.ws.rs.core.Response;
+import jakarta.ws.rs.core.MultivaluedMap;
+import jakarta.ws.rs.core.Response;
import java.util.List;
@@ -37,7 +37,7 @@
import static org.keycloak.provider.ProviderConfigProperty.BOOLEAN_TYPE;
import static org.keycloak.services.validation.Validation.FIELD_USERNAME;
-public class PhoneUsernamePasswordForm extends UsernamePasswordForm implements Authenticator, AuthenticatorFactory {
+public class PhoneUsernamePasswordForm extends UsernamePasswordForm implements AuthenticatorFactory {
private static final Logger logger = Logger.getLogger(PhoneUsernamePasswordForm.class);
@@ -51,29 +51,33 @@ public class PhoneUsernamePasswordForm extends UsernamePasswordForm implements A
/**
* use phone and password login
+ *
* @param context
* @return
*/
- private boolean isLoginWithPhoneNumber(AuthenticationFlowContext context){
+ private boolean isLoginWithPhoneNumber(AuthenticationFlowContext context) {
return context.getAuthenticatorConfig() == null ||
- context.getAuthenticatorConfig().getConfig().getOrDefault(CONFIG_IS_LOGIN_WITH_PHONE_NUMBER, "true").equals("true");
+ context.getAuthenticatorConfig().getConfig().getOrDefault(CONFIG_IS_LOGIN_WITH_PHONE_NUMBER, "true")
+ .equals("true");
}
/**
* use phone and verify code login
+ *
* @param context
* @return
*/
- private boolean isSupportPhone(AuthenticationFlowContext context){
+ private boolean isSupportPhone(AuthenticationFlowContext context) {
return context.getAuthenticatorConfig() == null ||
- context.getAuthenticatorConfig().getConfig().getOrDefault(CONFIG_IS_LOGIN_WITH_PHONE_VERIFY, "true").equals("true");
+ context.getAuthenticatorConfig().getConfig().getOrDefault(CONFIG_IS_LOGIN_WITH_PHONE_VERIFY, "true")
+ .equals("true");
}
- private LoginFormsProvider assemblyForm(AuthenticationFlowContext context, LoginFormsProvider form){
+ private LoginFormsProvider assemblyForm(AuthenticationFlowContext context, LoginFormsProvider form) {
if (isSupportPhone(context))
form.setAttribute(ATTRIBUTE_SUPPORT_PHONE, true);
- if (isLoginWithPhoneNumber(context)){
- form.setAttribute("loginWithPhoneNumber",true);
+ if (isLoginWithPhoneNumber(context)) {
+ form.setAttribute("loginWithPhoneNumber", true);
}
return form;
}
@@ -81,12 +85,13 @@ private LoginFormsProvider assemblyForm(AuthenticationFlowContext context, Login
@Override
protected Response challenge(AuthenticationFlowContext context, MultivaluedMap formData) {
LoginFormsProvider forms = context.form();
- if (formData.size() > 0) forms.setFormData(formData);
+ if (formData.size() > 0)
+ forms.setFormData(formData);
if (Utils.isDuplicatePhoneAllowed(context.getSession())) {
forms.setError("duplicatePhoneAllowedCantLogin");
logger.warn("duplicate phone allowed! phone login is disabled!");
} else {
- forms = assemblyForm(context,forms);
+ forms = assemblyForm(context, forms);
}
return forms.createLoginUsernamePassword();
}
@@ -104,11 +109,10 @@ protected boolean validateForm(AuthenticationFlowContext context, MultivaluedMap
}
String phoneNumber = inputData.getFirst(FIELD_PHONE_NUMBER);
-
if (Validation.isBlank(phoneNumber)) {
context.getEvent().error(Errors.USERNAME_MISSING);
context.form().setAttribute(ATTEMPTED_PHONE_ACTIVATED, true);
- assemblyForm(context,context.form());
+ assemblyForm(context, context.form());
Response challengeResponse = challenge(context, SupportPhonePages.Errors.MISSING.message(), FIELD_PHONE_NUMBER);
context.forceChallenge(challengeResponse);
return false;
@@ -120,7 +124,6 @@ protected boolean validateForm(AuthenticationFlowContext context, MultivaluedMap
return false;
}
-
return validatePhone(context, phoneNumber, code.trim());
}
@@ -129,8 +132,9 @@ private void invalidVerificationCode(AuthenticationFlowContext context, String n
context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
context.form().setAttribute(ATTEMPTED_PHONE_ACTIVATED, true)
.setAttribute(ATTEMPTED_PHONE_NUMBER, number);
- assemblyForm(context,context.form());
- Response challengeResponse = challenge(context, SupportPhonePages.Errors.NOT_MATCH.message(), FIELD_VERIFICATION_CODE);
+ assemblyForm(context, context.form());
+ Response challengeResponse = challenge(context, SupportPhonePages.Errors.NOT_MATCH.message(),
+ FIELD_VERIFICATION_CODE);
context.failureChallenge(AuthenticationFlowError.INVALID_CREDENTIALS, challengeResponse);
}
@@ -139,16 +143,18 @@ private boolean validatePhone(AuthenticationFlowContext context, String phoneNum
context.clearUser();
try {
- var validPhoneNumber = Utils.canonicalizePhoneNumber(context.getSession(),phoneNumber);
+ var validPhoneNumber = Utils.canonicalizePhoneNumber(context.getSession(), phoneNumber);
return Utils.findUserByPhone(context.getSession(), context.getRealm(), validPhoneNumber)
- .map(user -> validateVerificationCode(context, user, validPhoneNumber, code) && validateUser(context, user, validPhoneNumber))
+ .map(user -> validateVerificationCode(context, user, validPhoneNumber, code)
+ && validateUser(context, user, validPhoneNumber))
.orElseGet(() -> {
context.getEvent().error(Errors.USER_NOT_FOUND);
context.form().setAttribute(ATTEMPTED_PHONE_ACTIVATED, true)
.setAttribute(ATTEMPTED_PHONE_NUMBER, phoneNumber);
- assemblyForm(context,context.form());
- Response challengeResponse = challenge(context, SupportPhonePages.Errors.USER_NOT_FOUND.message(), FIELD_PHONE_NUMBER);
+ assemblyForm(context, context.form());
+ Response challengeResponse = challenge(context, SupportPhonePages.Errors.USER_NOT_FOUND.message(),
+ FIELD_PHONE_NUMBER);
context.failureChallenge(AuthenticationFlowError.INVALID_USER, challengeResponse);
return false;
});
@@ -156,14 +162,15 @@ private boolean validatePhone(AuthenticationFlowContext context, String phoneNum
context.getEvent().error(Errors.USERNAME_MISSING);
context.form().setAttribute(ATTEMPTED_PHONE_ACTIVATED, true)
.setAttribute(ATTEMPTED_PHONE_NUMBER, phoneNumber);
- assemblyForm(context,context.form());
- Response challengeResponse = challenge(context,e.getErrorType().message(), FIELD_PHONE_NUMBER);
+ assemblyForm(context, context.form());
+ Response challengeResponse = challenge(context, e.getErrorType().message(), FIELD_PHONE_NUMBER);
context.failureChallenge(AuthenticationFlowError.INVALID_USER, challengeResponse);
return false;
}
}
- private boolean validateVerificationCode(AuthenticationFlowContext context, UserModel user, String phoneNumber, String code) {
+ private boolean validateVerificationCode(AuthenticationFlowContext context, UserModel user, String phoneNumber,
+ String code) {
try {
context.getSession().getProvider(PhoneVerificationCodeProvider.class)
.validateCode(user, phoneNumber, code, TokenCodeType.AUTH);
@@ -184,7 +191,7 @@ private boolean isDisabledByBruteForce(AuthenticationFlowContext context, UserMo
context.getEvent().error(bruteForceError);
context.form().setAttribute(ATTEMPTED_PHONE_ACTIVATED, true)
.setAttribute(ATTEMPTED_PHONE_NUMBER, phoneNumber);
- assemblyForm(context,context.form());
+ assemblyForm(context, context.form());
Response challengeResponse = challenge(context, disabledByBruteForceError(), disabledByBruteForceFieldError());
context.forceChallenge(challengeResponse);
return true;
@@ -193,13 +200,14 @@ private boolean isDisabledByBruteForce(AuthenticationFlowContext context, UserMo
}
private boolean enabledUser(AuthenticationFlowContext context, UserModel user, String phoneNumber) {
- if (isDisabledByBruteForce(context, user, phoneNumber)) return false;
+ if (isDisabledByBruteForce(context, user, phoneNumber))
+ return false;
if (!user.isEnabled()) {
context.getEvent().user(user);
context.getEvent().error(Errors.USER_DISABLED);
context.form().setAttribute(ATTEMPTED_PHONE_ACTIVATED, true)
.setAttribute(ATTEMPTED_PHONE_NUMBER, phoneNumber);
- assemblyForm(context,context.form());
+ assemblyForm(context, context.form());
Response challengeResponse = challenge(context, Messages.ACCOUNT_DISABLED);
context.forceChallenge(challengeResponse);
return false;
@@ -216,7 +224,8 @@ private boolean validateUser(AuthenticationFlowContext context, UserModel user,
return true;
}
- private boolean validateUser(AuthenticationFlowContext context, UserModel user, MultivaluedMap inputData) {
+ private boolean validateUser(AuthenticationFlowContext context, UserModel user,
+ MultivaluedMap inputData) {
if (!enabledUser(context, user)) {
return false;
}
@@ -233,20 +242,23 @@ private boolean validateUser(AuthenticationFlowContext context, UserModel user,
}
@Override
- public boolean validateUserAndPassword(AuthenticationFlowContext context, MultivaluedMap inputData) {
+ public boolean validateUserAndPassword(AuthenticationFlowContext context, MultivaluedMap inputData) {
UserModel user = getUser(context, inputData);
boolean shouldClearUserFromCtxAfterBadPassword = !isUserAlreadySetBeforeUsernamePasswordAuth(context);
- return user != null && validatePassword(context, user, inputData, shouldClearUserFromCtxAfterBadPassword) && validateUser(context, user, inputData);
+ return user != null && validatePassword(context, user, inputData, shouldClearUserFromCtxAfterBadPassword)
+ && validateUser(context, user, inputData);
}
private UserModel getUser(AuthenticationFlowContext context, MultivaluedMap inputData) {
if (isUserAlreadySetBeforeUsernamePasswordAuth(context)) {
- // Get user from the authentication context in case he was already set before this authenticator
+ // Get user from the authentication context in case he was already set before
+ // this authenticator
UserModel user = context.getUser();
testInvalidUser(context, user);
return user;
} else {
- // Normal login. In this case this authenticator is supposed to establish identity of the user from the provided username
+ // Normal login. In this case this authenticator is supposed to establish
+ // identity of the user from the provided username
context.clearUser();
return getUserFromForm(context, inputData);
}
@@ -257,7 +269,7 @@ private UserModel getUserFromForm(AuthenticationFlowContext context, Multivalued
if (username == null) {
context.getEvent().error(Errors.USER_NOT_FOUND);
Response challengeResponse = challenge(context, getDefaultChallengeMessage(context), FIELD_USERNAME);
- assemblyForm(context,context.form());
+ assemblyForm(context, context.form());
context.failureChallenge(AuthenticationFlowError.INVALID_USER, challengeResponse);
return null;
}
@@ -273,7 +285,7 @@ private UserModel getUserFromForm(AuthenticationFlowContext context, Multivalued
user = KeycloakModelUtils.findUserByNameOrEmail(context.getSession(), context.getRealm(), username);
if (user == null &&
isLoginWithPhoneNumber(context) &&
- !Utils.isDuplicatePhoneAllowed(context.getSession())){
+ !Utils.isDuplicatePhoneAllowed(context.getSession())) {
user = Utils.findUserByPhone(context.getSession(), context.getRealm(), username).orElse(null);
}
} catch (ModelDuplicateException mde) {
@@ -281,9 +293,11 @@ private UserModel getUserFromForm(AuthenticationFlowContext context, Multivalued
// Could happen during federation import
if (mde.getDuplicateFieldName() != null && mde.getDuplicateFieldName().equals(UserModel.EMAIL)) {
- setDuplicateUserChallenge(context, Errors.EMAIL_IN_USE, Messages.EMAIL_EXISTS, AuthenticationFlowError.INVALID_USER);
+ setDuplicateUserChallenge(context, Errors.EMAIL_IN_USE, Messages.EMAIL_EXISTS,
+ AuthenticationFlowError.INVALID_USER);
} else {
- setDuplicateUserChallenge(context, Errors.USERNAME_IN_USE, Messages.USERNAME_EXISTS, AuthenticationFlowError.INVALID_USER);
+ setDuplicateUserChallenge(context, Errors.USERNAME_IN_USE, Messages.USERNAME_EXISTS,
+ AuthenticationFlowError.INVALID_USER);
}
return null;
}
@@ -302,7 +316,6 @@ public String getReferenceCategory() {
return PasswordCredentialModel.TYPE;
}
-
public static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
AuthenticationExecutionModel.Requirement.REQUIRED
};
@@ -340,6 +353,7 @@ public String getHelpText() {
.add()
.build();
}
+
@Override
public boolean isConfigurable() {
return true;
diff --git a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/browser/SmsOtpMfaAuthenticator.java b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/browser/SmsOtpMfaAuthenticator.java
index 9aa5c7d9..8c8fd02b 100644
--- a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/browser/SmsOtpMfaAuthenticator.java
+++ b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/browser/SmsOtpMfaAuthenticator.java
@@ -10,23 +10,19 @@
import cc.coopersoft.keycloak.phone.providers.constants.TokenCodeType;
import cc.coopersoft.keycloak.phone.providers.spi.PhoneProvider;
import org.jboss.logging.Logger;
-import org.jboss.resteasy.spi.HttpResponse;
import org.keycloak.authentication.AuthenticationFlowContext;
import org.keycloak.authentication.AuthenticationFlowError;
import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.CredentialValidator;
-import org.keycloak.common.util.ServerCookie;
import org.keycloak.credential.CredentialProvider;
+import org.keycloak.http.HttpResponse;
import org.keycloak.models.*;
-import org.keycloak.models.credential.dto.OTPSecretData;
import org.keycloak.services.validation.Validation;
-import org.keycloak.util.JsonSerialization;
-import javax.ws.rs.ForbiddenException;
-import javax.ws.rs.core.HttpHeaders;
-import javax.ws.rs.core.MultivaluedMap;
-import javax.ws.rs.core.Response;
-import java.io.IOException;
+import jakarta.ws.rs.ForbiddenException;
+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.Optional;
@@ -51,25 +47,21 @@ protected boolean validateCookie(AuthenticationFlowContext context) {
return false;
return Optional.of(context.getHttpRequest().getHttpHeaders().getCookies())
- .flatMap(cookies ->
- Optional.ofNullable(cookies.get("SMS_OTP_ANSWERED"))
- .flatMap(cookie -> OptionalUtils.ofBlank(cookie.getValue()))
- .flatMap(credentialId ->
- Optional.ofNullable(cookies.get(credentialId))
- .flatMap(cookie -> OptionalUtils.ofBlank(cookie.getValue()))
- .map(secret -> context.getUser()
- .credentialManager()
- .isValid(new UserCredentialModel(credentialId, getType(context.getSession()), secret)))
- )
- ).orElse(false);
+ .flatMap(cookies -> Optional.ofNullable(cookies.get("SMS_OTP_ANSWERED"))
+ .flatMap(cookie -> OptionalUtils.ofBlank(cookie.getValue()))
+ .flatMap(credentialId -> Optional.ofNullable(cookies.get(credentialId))
+ .flatMap(cookie -> OptionalUtils.ofBlank(cookie.getValue()))
+ .map(secret -> context.getUser()
+ .credentialManager()
+ .isValid(new UserCredentialModel(credentialId, getType(context.getSession()), secret)))))
+ .orElse(false);
}
protected void setCookie(AuthenticationFlowContext context, String credentialId, String secret) {
-
int maxCookieAge = Utils.getOtpExpires(context.getSession());
- if (maxCookieAge <= 0 ){
+ if (maxCookieAge <= 0) {
return;
}
@@ -91,20 +83,28 @@ protected void setCookie(AuthenticationFlowContext context, String credentialId,
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().getContextObject(HttpResponse.class);
- StringBuilder cookieBuf = new StringBuilder();
- ServerCookie.appendCookieValue(cookieBuf, 1, name, value, path, domain, comment, maxAge, secure, httpOnly, null);
- String cookie = cookieBuf.toString();
- response.getOutputHeaders().add(HttpHeaders.SET_COOKIE, cookie);
+ 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();
+ NewCookie newCookie = new NewCookie.Builder(name)
+ .value(value)
+ .path(path)
+ .domain(domain)
+ .version(1)
+ .comment(comment)
+ .maxAge(maxAge)
+ .secure(secure)
+ .build();
+ response.setCookieIfAbsent(newCookie);
}
@Override
public PhoneOtpCredentialProvider getCredentialProvider(KeycloakSession session) {
- return (PhoneOtpCredentialProvider) session.getProvider(CredentialProvider.class, PhoneOtpCredentialProviderFactory.PROVIDER_ID);
+ return (PhoneOtpCredentialProvider) session.getProvider(CredentialProvider.class,
+ PhoneOtpCredentialProviderFactory.PROVIDER_ID);
}
- private String getCredentialPhoneNumber(UserModel user){
+ private String getCredentialPhoneNumber(UserModel user) {
return PhoneOtpCredentialModel.getSmsOtpCredentialData(user)
.map(PhoneOtpCredentialModel.SmsOtpCredentialData::getPhoneNumber)
.orElseThrow(() -> new IllegalStateException("Not have OTP Credential"));
@@ -130,12 +130,12 @@ public void authenticate(AuthenticationFlowContext context) {
PhoneProvider phoneProvider = context.getSession().getProvider(PhoneProvider.class);
try {
- int expires = phoneProvider.sendTokenCode(phoneNumber,context.getConnection().getRemoteAddr(),
+ int expires = phoneProvider.sendTokenCode(phoneNumber, context.getConnection().getRemoteAddr(),
TokenCodeType.OTP, null);
context.form()
.setInfo("codeSent", phoneNumber)
.setAttribute("expires", expires)
- .setAttribute("initSend",true);
+ .setAttribute("initSend", true);
} catch (ForbiddenException e) {
logger.warn("otp send code Forbidden Exception!", e);
context.form().setError(SupportPhonePages.Errors.ABUSED.message());
@@ -144,10 +144,10 @@ public void authenticate(AuthenticationFlowContext context) {
context.form().setError(SupportPhonePages.Errors.FAIL.message());
}
- var credentialData = new PhoneOtpCredentialModel.SmsOtpCredentialData(phoneNumber,0);
- PhoneOtpCredentialModel.updateOtpCredential(context.getUser(),credentialData,null);
+ var credentialData = new PhoneOtpCredentialModel.SmsOtpCredentialData(phoneNumber, 0);
+ PhoneOtpCredentialModel.updateOtpCredential(context.getUser(), credentialData, null);
- Response challenge = challenge(context,phoneNumber);
+ Response challenge = challenge(context, phoneNumber);
context.challenge(challenge);
}
@@ -162,35 +162,36 @@ public void action(AuthenticationFlowContext context) {
if (credentialId == null || credentialId.isEmpty()) {
var defaultOtpCredential = getCredentialProvider(context.getSession())
.getDefaultCredential(context.getSession(), context.getRealm(), context.getUser());
- credentialId = defaultOtpCredential==null ? "" : defaultOtpCredential.getId();
+ credentialId = defaultOtpCredential == null ? "" : defaultOtpCredential.getId();
}
- if (Validation.isBlank(secret)){
+ if (Validation.isBlank(secret)) {
context.form()
.setError(SupportPhonePages.Errors.NOT_MATCH.message());
- Response challenge = challenge(context,phoneNumber);
+ Response challenge = challenge(context, phoneNumber);
context.failureChallenge(AuthenticationFlowError.INVALID_CREDENTIALS, challenge);
}
UserCredentialModel input = new UserCredentialModel(credentialId, getType(context.getSession()), secret);
- boolean validated = getCredentialProvider(context.getSession()).isValid(context.getRealm(), context.getUser(), input);
+ boolean validated = getCredentialProvider(context.getSession()).isValid(context.getRealm(), context.getUser(),
+ input);
if (!validated) {
context.form()
.setError(SupportPhonePages.Errors.NOT_MATCH.message());
- Response challenge = challenge(context,phoneNumber);
+ Response challenge = challenge(context, phoneNumber);
context.failureChallenge(AuthenticationFlowError.INVALID_CREDENTIALS, challenge);
return;
}
- setCookie(context,credentialId,secret);
+ setCookie(context, credentialId, secret);
context.success();
}
- protected Response challenge(AuthenticationFlowContext context,String phoneNumber) {
+ protected Response challenge(AuthenticationFlowContext context, String phoneNumber) {
return context.form()
.setAttribute(ATTRIBUTE_SUPPORT_PHONE, true)
- .setAttribute(SupportPhonePages.ATTEMPTED_PHONE_NUMBER,phoneNumber)
+ .setAttribute(SupportPhonePages.ATTEMPTED_PHONE_NUMBER, phoneNumber)
.createForm(PAGE);
}
diff --git a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/browser/SmsOtpMfaAuthenticatorFactory.java b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/browser/SmsOtpMfaAuthenticatorFactory.java
index a95aa2e6..d939dcd0 100644
--- a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/browser/SmsOtpMfaAuthenticatorFactory.java
+++ b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/browser/SmsOtpMfaAuthenticatorFactory.java
@@ -1,25 +1,20 @@
package cc.coopersoft.keycloak.phone.authentication.authenticators.browser;
-import cc.coopersoft.keycloak.phone.authentication.authenticators.resetcred.ResetCredentialWithPhone;
import org.keycloak.Config.Scope;
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.Collections;
import java.util.List;
-import static org.keycloak.provider.ProviderConfigProperty.STRING_TYPE;
-
-public class SmsOtpMfaAuthenticatorFactory implements AuthenticatorFactory, ConfigurableAuthenticatorFactory {
+public class SmsOtpMfaAuthenticatorFactory implements AuthenticatorFactory {
public final static String PROVIDER_ID = "sms-otp-authenticator";
-// public final static String COOKIE_MAX_AGE= "cookieMaxAge";
+ // public final static String COOKIE_MAX_AGE= "cookieMaxAge";
private final static SmsOtpMfaAuthenticator instance = new SmsOtpMfaAuthenticator();
@Override
@@ -54,11 +49,11 @@ public String getHelpText() {
@Override
public List getConfigProperties() {
-// ProviderConfigProperty rep =
-// new ProviderConfigProperty(COOKIE_MAX_AGE,
-// "Cookie Max Age",
-// "Max age in seconds of the SMS_OTP_COOKIE. Zero is Don't use Cookie",
-// STRING_TYPE, "3600");
+ // ProviderConfigProperty rep =
+ // new ProviderConfigProperty(COOKIE_MAX_AGE,
+ // "Cookie Max Age",
+ // "Max age in seconds of the SMS_OTP_COOKIE. Zero is Don't use Cookie",
+ // STRING_TYPE, "3600");
return null;
}
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 f0ad9bea..4669c6da 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
@@ -3,16 +3,14 @@
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;
-public class AuthenticationCodeAuthenticatorFactory implements AuthenticatorFactory, ConfigurableAuthenticatorFactory {
+public class AuthenticationCodeAuthenticatorFactory implements AuthenticatorFactory {
public static final String PROVIDER_ID = "verification-code-authenticator";
@@ -43,8 +41,9 @@ public Authenticator create(KeycloakSession session) {
private static AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
AuthenticationExecutionModel.Requirement.REQUIRED,
- //AuthenticationExecutionModel.Requirement.DISABLED
+ // AuthenticationExecutionModel.Requirement.DISABLED
};
+
@Override
public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
return REQUIREMENT_CHOICES;
@@ -60,24 +59,26 @@ public List getConfigProperties() {
return null;
}
-// private static final List configProperties = new ArrayList<>();
-
-// static {
-// ProviderConfigProperty maxAge;
-// maxAge = new ProviderConfigProperty();
-// maxAge.setName(MAX_AGE);
-// maxAge.setLabel("Verification Code Max Age");
-// maxAge.setType(ProviderConfigProperty.STRING_TYPE);
-// maxAge.setHelpText("Max age in seconds of the verification codes.");
-// configProperties.add(maxAge);
-// ProviderConfigProperty kind = new ProviderConfigProperty();
-// kind.setName(KIND);
-// kind.setLabel("Verification Code Kind");
-// kind.setType(ProviderConfigProperty.STRING_TYPE);
-// kind.setHelpText("a string that identifies what the verification code is used for, if this is set, " +
-// "a parameter of 'kind' is required to be equal with set value");
-// configProperties.add(kind);
-// }
+ // private static final List configProperties = new
+ // ArrayList<>();
+
+ // static {
+ // ProviderConfigProperty maxAge;
+ // maxAge = new ProviderConfigProperty();
+ // maxAge.setName(MAX_AGE);
+ // maxAge.setLabel("Verification Code Max Age");
+ // maxAge.setType(ProviderConfigProperty.STRING_TYPE);
+ // maxAge.setHelpText("Max age in seconds of the verification codes.");
+ // configProperties.add(maxAge);
+ // ProviderConfigProperty kind = new ProviderConfigProperty();
+ // kind.setName(KIND);
+ // kind.setLabel("Verification Code Kind");
+ // kind.setType(ProviderConfigProperty.STRING_TYPE);
+ // kind.setHelpText("a string that identifies what the verification code is used
+ // for, if this is set, " +
+ // "a parameter of 'kind' is required to be equal with set value");
+ // configProperties.add(kind);
+ // }
@Override
public String getDisplayType() {
@@ -99,4 +100,3 @@ public boolean isConfigurable() {
return false;
}
}
-
diff --git a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/directgrant/BaseDirectGrantAuthenticator.java b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/directgrant/BaseDirectGrantAuthenticator.java
index a2886235..01ff0cf7 100644
--- a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/directgrant/BaseDirectGrantAuthenticator.java
+++ b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/directgrant/BaseDirectGrantAuthenticator.java
@@ -10,8 +10,8 @@
import org.keycloak.models.UserModel;
import org.keycloak.representations.idm.OAuth2ErrorRepresentation;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
import java.util.Optional;
public abstract class BaseDirectGrantAuthenticator implements Authenticator {
@@ -21,33 +21,35 @@ public Response errorResponse(int status, String error, String errorDescription)
return Response.status(status).entity(errorRep).type(MediaType.APPLICATION_JSON_TYPE).build();
}
- protected Optional getPhoneNumber(AuthenticationFlowContext context){
+ protected Optional getPhoneNumber(AuthenticationFlowContext context) {
return OptionalUtils.ofBlank(OptionalUtils.ofBlank(
- context.getHttpRequest().getDecodedFormParameters().getFirst("phone_number"))
- .orElse(context.getHttpRequest().getDecodedFormParameters().getFirst("phoneNumber")));
+ context.getHttpRequest().getDecodedFormParameters().getFirst("phone_number"))
+ .orElse(context.getHttpRequest().getDecodedFormParameters().getFirst("phoneNumber")));
}
- protected Optional getAuthenticationCode(AuthenticationFlowContext context){
+ protected Optional getAuthenticationCode(AuthenticationFlowContext context) {
return OptionalUtils.ofBlank(context.getHttpRequest().getDecodedFormParameters().getFirst("code"));
}
- protected void invalidCredentials(AuthenticationFlowContext context,AuthenticationFlowError error){
+ protected void invalidCredentials(AuthenticationFlowContext context, AuthenticationFlowError error) {
context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
- Response challenge = errorResponse(Response.Status.UNAUTHORIZED.getStatusCode(), "invalid_grant", "Invalid user credentials");
+ Response challenge = errorResponse(Response.Status.UNAUTHORIZED.getStatusCode(), "invalid_grant",
+ "Invalid user credentials");
context.failure(error, challenge);
}
- protected void invalidCredentials(AuthenticationFlowContext context, UserModel user){
+ protected void invalidCredentials(AuthenticationFlowContext context, UserModel user) {
context.getEvent().user(user);
- invalidCredentials(context,AuthenticationFlowError.INVALID_CREDENTIALS);
+ invalidCredentials(context, AuthenticationFlowError.INVALID_CREDENTIALS);
}
- protected void invalidCredentials(AuthenticationFlowContext context){
- invalidCredentials(context,AuthenticationFlowError.INVALID_USER);
+ protected void invalidCredentials(AuthenticationFlowContext context) {
+ invalidCredentials(context, AuthenticationFlowError.INVALID_USER);
}
@Override
- public void close() {}
+ public void close() {
+ }
@Override
public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
diff --git a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/directgrant/EverybodyPhoneAuthenticator.java b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/directgrant/EverybodyPhoneAuthenticator.java
index 29a0c6f0..f6b2eb56 100644
--- a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/directgrant/EverybodyPhoneAuthenticator.java
+++ b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/directgrant/EverybodyPhoneAuthenticator.java
@@ -4,7 +4,6 @@
import cc.coopersoft.keycloak.phone.providers.representations.TokenCodeRepresentation;
import cc.coopersoft.keycloak.phone.providers.spi.PhoneVerificationCodeProvider;
import cc.coopersoft.keycloak.phone.Utils;
-import org.jboss.logging.Logger;
import org.keycloak.authentication.AuthenticationFlowContext;
import org.keycloak.authentication.AuthenticationFlowError;
import org.keycloak.models.KeycloakSession;
@@ -12,11 +11,8 @@
import org.keycloak.models.UserModel;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
-
public class EverybodyPhoneAuthenticator extends BaseDirectGrantAuthenticator {
- private static final Logger logger = Logger.getLogger(EverybodyPhoneAuthenticator.class);
-
public EverybodyPhoneAuthenticator(KeycloakSession session) {
if (session.getContext().getRealm() == null) {
throw new IllegalStateException("The service cannot accept a session without a realm in its context.");
@@ -37,13 +33,14 @@ public void setRequiredActions(KeycloakSession session, RealmModel realm, UserMo
public void authenticate(AuthenticationFlowContext context) {
getPhoneNumber(context)
.ifPresentOrElse(phoneNumber -> getAuthenticationCode(context)
- .ifPresentOrElse(code -> authToUser(context, phoneNumber, code),
- ()-> invalidCredentials(context)),
+ .ifPresentOrElse(code -> authToUser(context, phoneNumber, code),
+ () -> invalidCredentials(context)),
() -> invalidCredentials(context));
}
private void authToUser(AuthenticationFlowContext context, String phoneNumber, String code) {
- PhoneVerificationCodeProvider phoneVerificationCodeProvider = context.getSession().getProvider(PhoneVerificationCodeProvider.class);
+ PhoneVerificationCodeProvider phoneVerificationCodeProvider = context.getSession()
+ .getProvider(PhoneVerificationCodeProvider.class);
TokenCodeRepresentation tokenCode = phoneVerificationCodeProvider.ongoingProcess(phoneNumber, TokenCodeType.AUTH);
if (tokenCode == null || !tokenCode.getCode().equals(code)) {
@@ -53,7 +50,7 @@ private void authToUser(AuthenticationFlowContext context, String phoneNumber, S
UserModel user = Utils.findUserByPhone(context.getSession(), context.getRealm(), phoneNumber)
.orElseGet(() -> {
- if (context.getSession().users().getUserByUsername(context.getRealm(),phoneNumber) != null) {
+ if (context.getSession().users().getUserByUsername(context.getRealm(), phoneNumber) != null) {
invalidCredentials(context, AuthenticationFlowError.USER_CONFLICT);
return null;
}
@@ -65,7 +62,7 @@ private void authToUser(AuthenticationFlowContext context, String phoneNumber, S
});
if (user != null) {
context.setUser(user);
- phoneVerificationCodeProvider.tokenValidated(user, phoneNumber, tokenCode.getId(),false);
+ phoneVerificationCodeProvider.tokenValidated(user, phoneNumber, tokenCode.getId(), false);
context.success();
}
}
diff --git a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/directgrant/EverybodyPhoneAuthenticatorFactory.java b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/directgrant/EverybodyPhoneAuthenticatorFactory.java
index 953142bf..369357c7 100644
--- a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/directgrant/EverybodyPhoneAuthenticatorFactory.java
+++ b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/directgrant/EverybodyPhoneAuthenticatorFactory.java
@@ -3,7 +3,6 @@
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;
@@ -11,13 +10,12 @@
import java.util.List;
-public class EverybodyPhoneAuthenticatorFactory implements AuthenticatorFactory, ConfigurableAuthenticatorFactory {
-
+public class EverybodyPhoneAuthenticatorFactory implements AuthenticatorFactory {
public static final String PROVIDER_ID = "everybody-phone-authenticator";
@Override
- public Authenticator create(KeycloakSession session){
+ public Authenticator create(KeycloakSession session) {
return new EverybodyPhoneAuthenticator(session);
}
@@ -36,7 +34,6 @@ public void close() {
}
-
@Override
public String getDisplayType() {
return "Authentication everybody by phone";
diff --git a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/directgrant/PhoneNumberAuthenticator.java b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/directgrant/PhoneNumberAuthenticator.java
index 8b103453..244cf9a7 100644
--- a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/directgrant/PhoneNumberAuthenticator.java
+++ b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/directgrant/PhoneNumberAuthenticator.java
@@ -1,7 +1,6 @@
package cc.coopersoft.keycloak.phone.authentication.authenticators.directgrant;
import cc.coopersoft.keycloak.phone.Utils;
-import org.jboss.logging.Logger;
import org.keycloak.authentication.AuthenticationFlowContext;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
@@ -9,8 +8,6 @@
public class PhoneNumberAuthenticator extends BaseDirectGrantAuthenticator {
- private static final Logger logger = Logger.getLogger(PhoneNumberAuthenticator.class);
-
@Override
public boolean requiresUser() {
return false;
@@ -23,11 +20,12 @@ public void setRequiredActions(KeycloakSession session, RealmModel realm, UserMo
@Override
public void authenticate(AuthenticationFlowContext context) {
context.clearUser();
- getPhoneNumber(context).ifPresentOrElse(phoneNumber ->
- Utils.findUserByPhone(context.getSession(),context.getRealm(),phoneNumber)
- .ifPresentOrElse(user -> {
- context.setUser(user);
- context.success();
- },()->invalidCredentials(context)),() -> invalidCredentials(context));
+ getPhoneNumber(context).ifPresentOrElse(
+ phoneNumber -> Utils.findUserByPhone(context.getSession(), context.getRealm(), phoneNumber)
+ .ifPresentOrElse(user -> {
+ context.setUser(user);
+ context.success();
+ }, () -> invalidCredentials(context)),
+ () -> invalidCredentials(context));
}
}
diff --git a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/directgrant/PhoneNumberAuthenticatorFactory.java b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/directgrant/PhoneNumberAuthenticatorFactory.java
index 3e28517d..85303967 100644
--- a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/directgrant/PhoneNumberAuthenticatorFactory.java
+++ b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/directgrant/PhoneNumberAuthenticatorFactory.java
@@ -3,16 +3,14 @@
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;
-public class PhoneNumberAuthenticatorFactory implements AuthenticatorFactory, ConfigurableAuthenticatorFactory {
+public class PhoneNumberAuthenticatorFactory implements AuthenticatorFactory {
public static final String PROVIDER_ID = "phone-number-authenticator";
private static final PhoneNumberAuthenticator SINGLETON = new PhoneNumberAuthenticator();
@@ -81,4 +79,3 @@ public boolean isConfigurable() {
return false;
}
}
-
diff --git a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/resetcred/ResetCredentialWithPhone.java b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/resetcred/ResetCredentialWithPhone.java
index a2b5cbb5..8cd2f691 100644
--- a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/resetcred/ResetCredentialWithPhone.java
+++ b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/authenticators/resetcred/ResetCredentialWithPhone.java
@@ -23,8 +23,8 @@
import org.keycloak.services.messages.Messages;
import org.keycloak.services.validation.Validation;
-import javax.ws.rs.core.MultivaluedMap;
-import javax.ws.rs.core.Response;
+import jakarta.ws.rs.core.MultivaluedMap;
+import jakarta.ws.rs.core.Response;
import java.util.List;
@@ -44,22 +44,29 @@ public void authenticate(AuthenticationFlowContext context) {
String existingUserId = context.getAuthenticationSession().getAuthNote(AbstractIdpAuthenticator.EXISTING_USER_INFO);
if (existingUserId != null) {
- UserModel existingUser = AbstractIdpAuthenticator.getExistingUser(context.getSession(), context.getRealm(), context.getAuthenticationSession());
+ UserModel existingUser = AbstractIdpAuthenticator.getExistingUser(context.getSession(), context.getRealm(),
+ context.getAuthenticationSession());
- logger.debugf("Forget-password triggered when reauthenticating user after first broker login. Prefilling reset-credential-choose-user screen with user '%s' ", existingUser.getUsername());
+ logger.debugf(
+ "Forget-password triggered when reauthenticating user after first broker login. Prefilling reset-credential-choose-user screen with user '%s' ",
+ existingUser.getUsername());
context.setUser(existingUser);
Response challenge = context.form().createPasswordReset();
context.challenge(challenge);
return;
}
- String actionTokenUserId = context.getAuthenticationSession().getAuthNote(DefaultActionTokenKey.ACTION_TOKEN_USER_ID);
+ String actionTokenUserId = context.getAuthenticationSession()
+ .getAuthNote(DefaultActionTokenKey.ACTION_TOKEN_USER_ID);
if (actionTokenUserId != null) {
UserModel existingUser = context.getSession().users().getUserById(context.getRealm(), actionTokenUserId);
- // Action token logics handles checks for user ID validity and user being enabled
+ // Action token logics handles checks for user ID validity and user being
+ // enabled
- logger.debugf("Forget-password triggered when reauthenticating user after authentication via action token. Skipping reset-credential-choose-user screen and using user '%s' ", existingUser.getUsername());
+ logger.debugf(
+ "Forget-password triggered when reauthenticating user after authentication via action token. Skipping reset-credential-choose-user screen and using user '%s' ",
+ existingUser.getUsername());
context.setUser(existingUser);
context.success();
return;
@@ -83,8 +90,6 @@ public void action(AuthenticationFlowContext context) {
context.success();
}
-
-
protected boolean validateForm(AuthenticationFlowContext context, MultivaluedMap inputData) {
boolean byPhone = OptionalUtils
.ofBlank(inputData.getFirst(FIELD_PATH_PHONE_ACTIVATED))
@@ -97,7 +102,7 @@ protected boolean validateForm(AuthenticationFlowContext context, MultivaluedMap
String username = inputData.getFirst(AuthenticationManager.FORM_USERNAME);
UserModel user;
- if (!byPhone){
+ if (!byPhone) {
if (Validation.isBlank(username)) {
context.getEvent().error(Errors.USERNAME_MISSING);
Response challenge = challenge(context, Validation.FIELD_USERNAME, Messages.MISSING_USERNAME);
@@ -105,17 +110,18 @@ protected boolean validateForm(AuthenticationFlowContext context, MultivaluedMap
return false;
}
user = getUserByUsername(context, username.trim());
- }else{
+ } else {
if (Validation.isBlank(phoneNumber)) {
context.getEvent().error(Errors.USERNAME_MISSING);
- Response challenge = challenge(context, FIELD_PHONE_NUMBER, SupportPhonePages.Errors.MISSING.message(), phoneNumber);
+ Response challenge = challenge(context, FIELD_PHONE_NUMBER, SupportPhonePages.Errors.MISSING.message(),
+ phoneNumber);
context.forceChallenge(challenge);
return false;
}
try {
- phoneNumber = Utils.canonicalizePhoneNumber(context.getSession(),phoneNumber);
+ phoneNumber = Utils.canonicalizePhoneNumber(context.getSession(), phoneNumber);
} catch (PhoneNumberInvalidException e) {
context.getEvent().error(Errors.USERNAME_MISSING);
Response challenge = challenge(context, FIELD_PHONE_NUMBER,
@@ -124,7 +130,6 @@ protected boolean validateForm(AuthenticationFlowContext context, MultivaluedMap
return false;
}
-
String verificationCode = inputData.getFirst(FIELD_VERIFICATION_CODE);
if (Validation.isBlank(verificationCode)) {
invalidVerificationCode(context, phoneNumber);
@@ -133,12 +138,12 @@ protected boolean validateForm(AuthenticationFlowContext context, MultivaluedMap
user = Utils.findUserByPhone(context.getSession(), context.getRealm(), phoneNumber)
.orElse(null);
- if (user != null && !validateVerificationCode(context, user, phoneNumber, verificationCode.trim())){
+ if (user != null && !validateVerificationCode(context, user, phoneNumber, verificationCode.trim())) {
return false;
}
}
- return validateUser(context,user,byPhone,byPhone ? phoneNumber : username);
+ return validateUser(context, user, byPhone, byPhone ? phoneNumber : username);
}
protected UserModel getUserByUsername(AuthenticationFlowContext context, String username) {
@@ -176,31 +181,35 @@ protected boolean isDisabledByBruteForce(AuthenticationFlowContext context, User
}
protected boolean validateUser(AuthenticationFlowContext context,
- UserModel user, boolean byPhone, String attempted) {
+ UserModel user, boolean byPhone, String attempted) {
- if (user == null){
+ if (user == null) {
context.getEvent().error(Errors.USER_NOT_FOUND);
- if(!byPhone){
+ if (!byPhone) {
context.getEvent().detail(Details.USERNAME, attempted);
}
- Response challenge = byPhone ? challenge(context, FIELD_PHONE_NUMBER, SupportPhonePages.Errors.USER_NOT_FOUND.message(), attempted) : challenge(context, Validation.FIELD_USERNAME, Messages.INVALID_USERNAME_OR_EMAIL);
+ Response challenge = byPhone
+ ? challenge(context, FIELD_PHONE_NUMBER, SupportPhonePages.Errors.USER_NOT_FOUND.message(), attempted)
+ : challenge(context, Validation.FIELD_USERNAME, Messages.INVALID_USERNAME_OR_EMAIL);
context.forceChallenge(challenge);
return false;
}
- if (byPhone ? isDisabledByBruteForce(context, user, attempted) : isDisabledByBruteForce(context, user)) return false;
+ if (byPhone ? isDisabledByBruteForce(context, user, attempted) : isDisabledByBruteForce(context, user))
+ return false;
if (!user.isEnabled()) {
- if(!byPhone){
+ if (!byPhone) {
context.getEvent().detail(Details.USERNAME, attempted);
}
context.getEvent().user(user);
context.getEvent().error(Errors.USER_DISABLED);
- Response challenge = byPhone ? challenge(context,FIELD_PHONE_NUMBER, Errors.USER_DISABLED, attempted) : challenge(context,Validation.FIELD_USERNAME,Errors.USER_DISABLED);
+ Response challenge = byPhone ? challenge(context, FIELD_PHONE_NUMBER, Errors.USER_DISABLED, attempted)
+ : challenge(context, Validation.FIELD_USERNAME, Errors.USER_DISABLED);
context.forceChallenge(challenge);
return false;
}
- if (byPhone){
+ if (byPhone) {
context.getAuthenticationSession().setAuthNote(SHOULD_SEND_EMAIL, "false");
}
context.setUser(user);
@@ -209,13 +218,14 @@ protected boolean validateUser(AuthenticationFlowContext context,
protected void invalidVerificationCode(AuthenticationFlowContext context, String phoneNumber) {
context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
- Response challenge = challenge(context, FIELD_VERIFICATION_CODE, SupportPhonePages.Errors.NOT_MATCH.message(), phoneNumber);
+ Response challenge = challenge(context, FIELD_VERIFICATION_CODE, SupportPhonePages.Errors.NOT_MATCH.message(),
+ phoneNumber);
context.failureChallenge(AuthenticationFlowError.INVALID_CREDENTIALS, challenge);
}
protected Response challenge(AuthenticationFlowContext context,
- String field, String message,
- String phoneNumber) {
+ String field, String message,
+ String phoneNumber) {
return context.form()
.addError(new FormMessage(field, message))
.setAttribute(ATTRIBUTE_SUPPORT_PHONE, true)
@@ -225,16 +235,18 @@ protected Response challenge(AuthenticationFlowContext context,
}
protected Response challenge(AuthenticationFlowContext context,
- String field, String message) {
+ String field, String message) {
return context.form()
.addError(new FormMessage(field, message))
.setAttribute(ATTRIBUTE_SUPPORT_PHONE, true)
.createPasswordReset();
}
- private boolean validateVerificationCode(AuthenticationFlowContext context, UserModel user, String phoneNumber, String code) {
+ private boolean validateVerificationCode(AuthenticationFlowContext context, UserModel user, String phoneNumber,
+ String code) {
try {
- context.getSession().getProvider(PhoneVerificationCodeProvider.class).validateCode(user, phoneNumber, code, TokenCodeType.RESET);
+ context.getSession().getProvider(PhoneVerificationCodeProvider.class).validateCode(user, phoneNumber, code,
+ TokenCodeType.RESET);
logger.debug("verification code success!");
return true;
} catch (Exception e) {
@@ -245,7 +257,6 @@ private boolean validateVerificationCode(AuthenticationFlowContext context, User
}
}
-
@Override
public boolean requiresUser() {
return false;
@@ -261,7 +272,6 @@ public void setRequiredActions(KeycloakSession session, RealmModel realm, UserMo
}
-
@Override
public String getDisplayType() {
return "Reset Credential Choose User with Phone";
diff --git a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/forms/RegistrationPhoneUserCreation.java b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/forms/RegistrationPhoneUserCreation.java
index add35422..6fa42539 100644
--- a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/forms/RegistrationPhoneUserCreation.java
+++ b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/forms/RegistrationPhoneUserCreation.java
@@ -24,7 +24,7 @@
import org.keycloak.userprofile.UserProfileProvider;
import org.keycloak.userprofile.ValidationException;
-import javax.ws.rs.core.MultivaluedMap;
+import jakarta.ws.rs.core.MultivaluedMap;
import java.util.ArrayList;
import java.util.List;
@@ -32,7 +32,7 @@
import static org.keycloak.provider.ProviderConfigProperty.BOOLEAN_TYPE;
/**
- * replace org.keycloak.authentication.forms.RegistrationUserCreation.java
+ * replace org.keycloak.authentication.forms.RegistrationUserCreation.java
*/
public class RegistrationPhoneUserCreation implements FormActionFactory, FormAction {
@@ -42,12 +42,11 @@ public class RegistrationPhoneUserCreation implements FormActionFactory, FormAct
public static final String CONFIG_PHONE_NUMBER_AS_USERNAME = "phoneNumberAsUsername";
- public static final String CONFIG_INPUT_NAME="isInputName";
+ public static final String CONFIG_INPUT_NAME = "isInputName";
- public static final String CONFIG_INPUT_EMAIL="isInputEmail";
+ public static final String CONFIG_INPUT_EMAIL = "isInputEmail";
private static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
- AuthenticationExecutionModel.Requirement.REQUIRED, AuthenticationExecutionModel.Requirement.DISABLED};
-
+ AuthenticationExecutionModel.Requirement.REQUIRED, AuthenticationExecutionModel.Requirement.DISABLED };
@Override
public String getDisplayType() {
@@ -76,7 +75,8 @@ public boolean isConfigurable() {
.property().name(CONFIG_PHONE_NUMBER_AS_USERNAME)
.type(BOOLEAN_TYPE)
.label("Phone number as username")
- .helpText("Allow users to set phone number as username. If Realm has `email as username` set to true, this is invalid!")
+ .helpText(
+ "Allow users to set phone number as username. If Realm has `email as username` set to true, this is invalid!")
.defaultValue(true)
.add()
.property().name(CONFIG_INPUT_NAME)
@@ -104,7 +104,6 @@ public boolean isUserSetupAllowed() {
return false;
}
-
@Override
public List getConfigProperties() {
return CONFIG_PROPERTIES;
@@ -136,15 +135,15 @@ public String getId() {
// FormAction
- private boolean isPhoneNumberAsUsername(FormContext context){
+ private boolean isPhoneNumberAsUsername(FormContext context) {
if (context.getAuthenticatorConfig() == null || "true".equals(context.getAuthenticatorConfig().getConfig()
- .getOrDefault(CONFIG_PHONE_NUMBER_AS_USERNAME,"true"))){
+ .getOrDefault(CONFIG_PHONE_NUMBER_AS_USERNAME, "true"))) {
- if (context.getRealm().isRegistrationEmailAsUsername()){
+ if (context.getRealm().isRegistrationEmailAsUsername()) {
logger.warn("Realm set email as username, can't use phone number.");
return false;
}
- if (Utils.isDuplicatePhoneAllowed(context.getSession())){
+ if (Utils.isDuplicatePhoneAllowed(context.getSession())) {
logger.warn("Duplicate phone allowed! phone number can't be used as username.");
return false;
}
@@ -153,19 +152,19 @@ private boolean isPhoneNumberAsUsername(FormContext context){
return false;
}
- private boolean isHideName(FormContext context){
+ private boolean isHideName(FormContext context) {
return context.getAuthenticatorConfig() == null ||
!"true".equalsIgnoreCase(context.getAuthenticatorConfig().getConfig()
- .getOrDefault(CONFIG_INPUT_NAME,"true"));
+ .getOrDefault(CONFIG_INPUT_NAME, "true"));
}
- private boolean isHideEmail(FormContext context){
+ private boolean isHideEmail(FormContext context) {
if (context.getAuthenticatorConfig() == null ||
"true".equalsIgnoreCase(context.getAuthenticatorConfig().getConfig()
- .getOrDefault(CONFIG_INPUT_EMAIL,"true"))){
+ .getOrDefault(CONFIG_INPUT_EMAIL, "true"))) {
return false;
}
- if (context.getRealm().isRegistrationEmailAsUsername()){
+ if (context.getRealm().isRegistrationEmailAsUsername()) {
logger.warn("`email as username` is set, so can't hide email input.");
return false;
}
@@ -178,15 +177,15 @@ public void buildPage(FormContext context, LoginFormsProvider form) {
form.setAttribute("phoneNumberRequired", true);
- if (isPhoneNumberAsUsername(context)){
+ if (isPhoneNumberAsUsername(context)) {
form.setAttribute("registrationPhoneNumberAsUsername", true);
}
- if (isHideName(context)){
+ if (isHideName(context)) {
form.setAttribute("hideName", true);
}
- if (isHideEmail(context)){
+ if (isHideEmail(context)) {
form.setAttribute("hideEmail", true);
}
}
@@ -196,7 +195,6 @@ public void validate(ValidationContext context) {
KeycloakSession session = context.getSession();
-
MultivaluedMap formData = context.getHttpRequest().getDecodedFormParameters();
context.getEvent().detail(Details.REGISTER_METHOD, "form");
@@ -209,9 +207,9 @@ public void validate(ValidationContext context) {
context.error(Errors.INVALID_REGISTRATION);
context.validationError(formData, errors);
success = false;
- }else {
+ } else {
try {
- phoneNumber = Utils.canonicalizePhoneNumber(session,phoneNumber);
+ phoneNumber = Utils.canonicalizePhoneNumber(session, phoneNumber);
if (!Utils.isDuplicatePhoneAllowed(session) &&
Utils.findUserByPhone(session, context.getRealm(), phoneNumber).isPresent()) {
context.error(Errors.INVALID_REGISTRATION);
@@ -229,31 +227,31 @@ public void validate(ValidationContext context) {
}
context.getEvent().detail(FIELD_PHONE_NUMBER, phoneNumber);
- if (isPhoneNumberAsUsername(context)){
+ if (isPhoneNumberAsUsername(context)) {
context.getEvent().detail(Details.USERNAME, phoneNumber);
- formData.putSingle(UserModel.USERNAME,phoneNumber);
+ formData.putSingle(UserModel.USERNAME, phoneNumber);
}
UserProfileProvider profileProvider = session.getProvider(UserProfileProvider.class);
- UserProfile profile = profileProvider.create(UserProfileContext.REGISTRATION_USER_CREATION, formData);
+ UserProfile profile = profileProvider.create(UserProfileContext.REGISTRATION, formData);
- String username = profile.getAttributes().getFirstValue(UserModel.USERNAME);
+ String username = profile.getAttributes().getFirst(UserModel.USERNAME);
context.getEvent().detail(Details.USERNAME, username);
boolean hideName = isHideName(context);
boolean hideEmail = isHideEmail(context);
- if (!hideName){
- String firstName = profile.getAttributes().getFirstValue(UserModel.FIRST_NAME);
- String lastName = profile.getAttributes().getFirstValue(UserModel.LAST_NAME);
+ if (!hideName) {
+ String firstName = profile.getAttributes().getFirst(UserModel.FIRST_NAME);
+ String lastName = profile.getAttributes().getFirst(UserModel.LAST_NAME);
context.getEvent().detail(Details.FIRST_NAME, firstName);
context.getEvent().detail(Details.LAST_NAME, lastName);
}
- if (!hideEmail){
- String email = profile.getAttributes().getFirstValue(UserModel.EMAIL);
+ if (!hideEmail) {
+ String email = profile.getAttributes().getFirst(UserModel.EMAIL);
context.getEvent().detail(Details.EMAIL, email);
- if (context.getRealm().isRegistrationEmailAsUsername()){
+ if (context.getRealm().isRegistrationEmailAsUsername()) {
context.getEvent().detail(Details.USERNAME, email);
}
}
@@ -275,7 +273,7 @@ public void validate(ValidationContext context) {
if (success) {
context.success();
}
- context.validationError(formData,errors);
+ context.validationError(formData, errors);
}
@Override
@@ -289,37 +287,38 @@ public void success(FormContext context) {
var session = context.getSession();
try {
- phoneNumber = Utils.canonicalizePhoneNumber(session,phoneNumber);
+ phoneNumber = Utils.canonicalizePhoneNumber(session, phoneNumber);
} catch (PhoneNumberInvalidException e) {
// verified in validate process
throw new IllegalStateException();
}
- if (context.getRealm().isRegistrationEmailAsUsername()){
+ if (context.getRealm().isRegistrationEmailAsUsername()) {
username = email;
- } else if (isPhoneNumberAsUsername(context)){
+ } else if (isPhoneNumberAsUsername(context)) {
username = phoneNumber;
- formData.add(UserModel.USERNAME,phoneNumber);
+ formData.add(UserModel.USERNAME, phoneNumber);
}
context.getEvent().detail(Details.USERNAME, username)
.detail(Details.REGISTER_METHOD, "form")
- .detail(FIELD_PHONE_NUMBER,phoneNumber);
+ .detail(FIELD_PHONE_NUMBER, phoneNumber);
- if (!isHideEmail(context)){
- context.getEvent().detail(Details.EMAIL,email);
+ if (!isHideEmail(context)) {
+ context.getEvent().detail(Details.EMAIL, email);
}
UserProfileProvider profileProvider = session.getProvider(UserProfileProvider.class);
- UserProfile profile = profileProvider.create(UserProfileContext.REGISTRATION_USER_CREATION, formData);
+ UserProfile profile = profileProvider.create(UserProfileContext.REGISTRATION, formData);
UserModel user = profile.create();
-// UserModel user = context.getSession().users().addUser(context.getRealm(), username);
+ // UserModel user = context.getSession().users().addUser(context.getRealm(),
+ // username);
user.setEnabled(true);
context.setUser(user);
context.getAuthenticationSession().setClientNote(OIDCLoginProtocol.LOGIN_HINT_PARAM, username);
- //AttributeFormDataProcessor.process(formData);
+ // AttributeFormDataProcessor.process(formData);
context.getEvent().user(user);
context.getEvent().success();
@@ -342,7 +341,7 @@ public boolean requiresUser() {
@Override
public boolean configuredFor(KeycloakSession keycloakSession, RealmModel realmModel, UserModel userModel) {
- return true;//!realmModel.isRegistrationEmailAsUsername();
+ return true;// !realmModel.isRegistrationEmailAsUsername();
}
@Override
diff --git a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/forms/RegistrationPhoneVerificationCode.java b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/forms/RegistrationPhoneVerificationCode.java
index 22c9c6d3..b818f88f 100644
--- a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/forms/RegistrationPhoneVerificationCode.java
+++ b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/forms/RegistrationPhoneVerificationCode.java
@@ -8,8 +8,6 @@
import cc.coopersoft.keycloak.phone.providers.exception.PhoneNumberInvalidException;
import cc.coopersoft.keycloak.phone.providers.representations.TokenCodeRepresentation;
import cc.coopersoft.keycloak.phone.providers.spi.PhoneVerificationCodeProvider;
-import cc.coopersoft.keycloak.phone.providers.spi.PhoneProvider;
-import com.google.i18n.phonenumbers.NumberParseException;
import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.authentication.FormAction;
@@ -27,7 +25,7 @@
import org.keycloak.provider.ProviderConfigurationBuilder;
import org.keycloak.services.validation.Validation;
-import javax.ws.rs.core.MultivaluedMap;
+import jakarta.ws.rs.core.MultivaluedMap;
import java.util.ArrayList;
import java.util.List;
@@ -40,7 +38,7 @@ public class RegistrationPhoneVerificationCode implements FormAction, FormAction
public static final String PROVIDER_ID = "registration-phone";
- public static final String CONFIG_OPT_CREDENTIAL="createOPTCredential";
+ public static final String CONFIG_OPT_CREDENTIAL = "createOPTCredential";
@Override
public String getHelpText() {
@@ -91,7 +89,7 @@ public List getConfigProperties() {
}
private final static Requirement[] REQUIREMENT_CHOICES = {
- Requirement.REQUIRED, Requirement.DISABLED};
+ Requirement.REQUIRED, Requirement.DISABLED };
@Override
public Requirement[] getRequirementChoices() {
@@ -118,7 +116,6 @@ public String getId() {
return PROVIDER_ID;
}
-
// FormAction
private PhoneVerificationCodeProvider getTokenCodeService(KeycloakSession session) {
@@ -128,7 +125,6 @@ private PhoneVerificationCodeProvider getTokenCodeService(KeycloakSession sessio
@Override
public void validate(ValidationContext context) {
-
MultivaluedMap formData = context.getHttpRequest().getDecodedFormParameters();
List errors = new ArrayList<>();
context.getEvent().detail(Details.REGISTER_METHOD, "form");
@@ -137,7 +133,7 @@ public void validate(ValidationContext context) {
String phoneNumber = formData.getFirst(FIELD_PHONE_NUMBER);
- if (Validation.isBlank(phoneNumber)){
+ if (Validation.isBlank(phoneNumber)) {
context.error(Errors.INVALID_REGISTRATION);
errors.add(new FormMessage(FIELD_PHONE_NUMBER, SupportPhonePages.Errors.MISSING));
context.validationError(formData, errors);
@@ -145,7 +141,7 @@ public void validate(ValidationContext context) {
}
try {
- phoneNumber = Utils.canonicalizePhoneNumber(context.getSession(),phoneNumber);
+ phoneNumber = Utils.canonicalizePhoneNumber(context.getSession(), phoneNumber);
} catch (PhoneNumberInvalidException e) {
context.error(Errors.INVALID_REGISTRATION);
errors.add(new FormMessage(FIELD_PHONE_NUMBER, e.getErrorType().message()));
@@ -156,7 +152,8 @@ public void validate(ValidationContext context) {
context.getEvent().detail(FIELD_PHONE_NUMBER, phoneNumber);
String verificationCode = formData.getFirst(FIELD_VERIFICATION_CODE);
- TokenCodeRepresentation tokenCode = getTokenCodeService(session).ongoingProcess(phoneNumber, TokenCodeType.REGISTRATION);
+ TokenCodeRepresentation tokenCode = getTokenCodeService(session).ongoingProcess(phoneNumber,
+ TokenCodeType.REGISTRATION);
if (Validation.isBlank(verificationCode) || tokenCode == null || !tokenCode.getCode().equals(verificationCode)) {
context.error(Errors.INVALID_REGISTRATION);
formData.remove(FIELD_VERIFICATION_CODE);
@@ -180,23 +177,24 @@ public void success(FormContext context) {
String phoneNumber = formData.getFirst(FIELD_PHONE_NUMBER);
try {
- phoneNumber = Utils.canonicalizePhoneNumber(context.getSession(),phoneNumber);
+ phoneNumber = Utils.canonicalizePhoneNumber(context.getSession(), phoneNumber);
} catch (PhoneNumberInvalidException e) {
- //verified in validate process
+ // verified in validate process
throw new IllegalStateException();
}
String tokenId = session.getAttribute("tokenId", String.class);
logger.info(String.format("registration user %s phone success, tokenId is: %s", user.getId(), tokenId));
- getTokenCodeService(context.getSession()).tokenValidated(user, phoneNumber, tokenId,false);
+ getTokenCodeService(context.getSession()).tokenValidated(user, phoneNumber, tokenId, false);
AuthenticatorConfigModel config = context.getAuthenticatorConfig();
if (config != null &&
- "true".equalsIgnoreCase(config.getConfig().getOrDefault(CONFIG_OPT_CREDENTIAL,"false"))){
+ "true".equalsIgnoreCase(config.getConfig().getOrDefault(CONFIG_OPT_CREDENTIAL, "false"))) {
PhoneOtpCredentialProvider ocp = (PhoneOtpCredentialProvider) context.getSession()
.getProvider(CredentialProvider.class, PhoneOtpCredentialProviderFactory.PROVIDER_ID);
- ocp.createCredential(context.getRealm(), context.getUser(), PhoneOtpCredentialModel.create(phoneNumber,tokenId,0));
+ ocp.createCredential(context.getRealm(), context.getUser(),
+ PhoneOtpCredentialModel.create(phoneNumber, tokenId, 0));
}
}
diff --git a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/forms/RegistrationRedirectParametersReader.java b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/forms/RegistrationRedirectParametersReader.java
index 2e534a55..9de2856a 100644
--- a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/forms/RegistrationRedirectParametersReader.java
+++ b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/forms/RegistrationRedirectParametersReader.java
@@ -17,7 +17,6 @@
import java.util.regex.Pattern;
import static org.keycloak.provider.ProviderConfigProperty.MULTIVALUED_STRING_TYPE;
-import static org.keycloak.provider.ProviderConfigProperty.STRING_TYPE;
public class RegistrationRedirectParametersReader implements FormActionFactory, FormAction {
@@ -26,10 +25,8 @@ public class RegistrationRedirectParametersReader implements FormActionFactory,
public static final String PROVIDER_ID = "registration-redirect-parameter";
public static final String PARAM_NAMES = "acceptParameter";
-
private static AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
- AuthenticationExecutionModel.Requirement.REQUIRED, AuthenticationExecutionModel.Requirement.DISABLED};
-
+ AuthenticationExecutionModel.Requirement.REQUIRED, AuthenticationExecutionModel.Requirement.DISABLED };
@Override
public String getDisplayType() {
@@ -48,11 +45,10 @@ public boolean isConfigurable() {
@Override
public List getConfigProperties() {
- ProviderConfigProperty rep =
- new ProviderConfigProperty(PARAM_NAMES,
- "Accept query param",
- "Registration query param accept names.",
- MULTIVALUED_STRING_TYPE, null);
+ ProviderConfigProperty rep = new ProviderConfigProperty(PARAM_NAMES,
+ "Accept query param",
+ "Registration query param accept names.",
+ MULTIVALUED_STRING_TYPE, null);
return Collections.singletonList(rep);
}
@@ -110,7 +106,6 @@ public void validate(ValidationContext validationContext) {
@Override
public void success(FormContext context) {
-
String redirectUri = context.getAuthenticationSession().getRedirectUri();
logger.info("add user attribute form redirectUri:" + redirectUri);
if (Validation.isBlank(redirectUri)) {
@@ -123,7 +118,8 @@ public void success(FormContext context) {
logger.error("redirectUri is null");
return;
}
- //url.queryParameterNames().forEach(s -> logger.info("redirect param name ->" + s));
+ // url.queryParameterNames().forEach(s -> logger.info("redirect param name ->" +
+ // s));
UserModel user = context.getUser();
AuthenticatorConfigModel authenticatorConfig = context.getAuthenticatorConfig();
diff --git a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/requiredactions/ConfigSmsOtpRequiredAction.java b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/requiredactions/ConfigSmsOtpRequiredAction.java
index c5b5bbca..94200d7f 100644
--- a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/requiredactions/ConfigSmsOtpRequiredAction.java
+++ b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/requiredactions/ConfigSmsOtpRequiredAction.java
@@ -8,89 +8,95 @@
import cc.coopersoft.keycloak.phone.providers.constants.TokenCodeType;
import cc.coopersoft.keycloak.phone.providers.exception.PhoneNumberInvalidException;
import cc.coopersoft.keycloak.phone.providers.spi.PhoneVerificationCodeProvider;
-import cc.coopersoft.keycloak.phone.providers.spi.PhoneProvider;
-import com.google.i18n.phonenumbers.NumberParseException;
import org.keycloak.authentication.RequiredActionContext;
import org.keycloak.authentication.RequiredActionProvider;
-import org.keycloak.credential.CredentialModel;
import org.keycloak.credential.CredentialProvider;
-import javax.ws.rs.BadRequestException;
-import javax.ws.rs.ForbiddenException;
-import javax.ws.rs.core.Response;
+import jakarta.ws.rs.BadRequestException;
+import jakarta.ws.rs.ForbiddenException;
+import jakarta.ws.rs.core.Response;
import java.util.Optional;
import static cc.coopersoft.keycloak.phone.authentication.authenticators.browser.PhoneUsernamePasswordForm.VERIFIED_PHONE_NUMBER;
public class ConfigSmsOtpRequiredAction implements RequiredActionProvider {
- public static final String PROVIDER_ID = "CONFIGURE_SMS_OTP";
-
- @Override
- public void evaluateTriggers(RequiredActionContext context) {
- }
-
-// private Optional getOTPCredential(RequiredActionContext context){
-// return Optional.ofNullable(context.getUser())
-// .flatMap(user -> user.credentialManager().getStoredCredentialsByTypeStream(PhoneOtpCredentialModel.TYPE).findFirst())
-// .map(credentialModel -> (PhoneOtpCredentialModel) credentialModel);
-// }
-
- @Override
- public void requiredActionChallenge(RequiredActionContext context) {
-
- var userPhoneNumber = PhoneOtpCredentialModel.getSmsOtpCredentialData(context.getUser())
- .map(PhoneOtpCredentialModel.SmsOtpCredentialData::getPhoneNumber)
- .orElseGet(() -> Optional.ofNullable(context.getUser())
- .flatMap(user -> Optional.ofNullable(user.getFirstAttribute(SupportPhonePages.FIELD_PHONE_NUMBER)))
- .orElse(null)
- );
-
- Response challenge = context.form()
- .setAttribute(SupportPhonePages.FIELD_PHONE_NUMBER, userPhoneNumber)
- .createForm("login-sms-otp-config.ftl");
- context.challenge(challenge);
- }
-
- @Override
- public void processAction(RequiredActionContext context) {
- var session = context.getSession();
- PhoneVerificationCodeProvider phoneVerificationCodeProvider = session.getProvider(PhoneVerificationCodeProvider.class);
- String phoneNumber = context.getHttpRequest().getDecodedFormParameters().getFirst(SupportPhonePages.FIELD_PHONE_NUMBER);
- String code = context.getHttpRequest().getDecodedFormParameters().getFirst(SupportPhonePages.FIELD_VERIFICATION_CODE);
- try {
- phoneNumber = Utils.canonicalizePhoneNumber(context.getSession(),phoneNumber);
- phoneVerificationCodeProvider.validateCode(context.getUser(), phoneNumber, code, TokenCodeType.OTP);
-
- PhoneOtpCredentialProvider ocp = (PhoneOtpCredentialProvider) context.getSession()
- .getProvider(CredentialProvider.class, PhoneOtpCredentialProviderFactory.PROVIDER_ID);
- ocp.createCredential(context.getRealm(), context.getUser(),
- PhoneOtpCredentialModel.create(phoneNumber,code,Utils.getOtpExpires(context.getSession())));
- context.getAuthenticationSession().setAuthNote(VERIFIED_PHONE_NUMBER, phoneNumber);
- context.success();
- } catch (BadRequestException e) {
-
- Response challenge = context.form()
- .setError(SupportPhonePages.Errors.NO_PROCESS.message())
- .createForm("login-sms-otp-config.ftl");
- context.challenge(challenge);
-
- } catch (ForbiddenException e) {
-
- Response challenge = context.form()
- .setAttribute("phoneNumber", phoneNumber)
- .setError(SupportPhonePages.Errors.NOT_MATCH.message())
- .createForm("login-sms-otp-config.ftl");
- context.challenge(challenge);
- } catch (PhoneNumberInvalidException e) {
- Response challenge = context.form()
- .setError(e.getErrorType().message())
- .createForm("login-sms-otp-config.ftl");
- context.challenge(challenge);
+ public static final String PROVIDER_ID = "CONFIGURE_SMS_OTP";
+
+ @Override
+ public void evaluateTriggers(RequiredActionContext context) {
+ }
+
+ // private Optional
+ // getOTPCredential(RequiredActionContext context){
+ // return Optional.ofNullable(context.getUser())
+ // .flatMap(user ->
+ // user.credentialManager().getStoredCredentialsByTypeStream(PhoneOtpCredentialModel.TYPE).findFirst())
+ // .map(credentialModel -> (PhoneOtpCredentialModel) credentialModel);
+ // }
+
+ @Override
+ public void requiredActionChallenge(RequiredActionContext context) {
+
+ var userPhoneNumber = PhoneOtpCredentialModel.getSmsOtpCredentialData(context.getUser())
+ .map(PhoneOtpCredentialModel.SmsOtpCredentialData::getPhoneNumber)
+ .orElseGet(() -> Optional.ofNullable(context.getUser())
+ .flatMap(user -> Optional
+ .ofNullable(user.getFirstAttribute(
+ SupportPhonePages.FIELD_PHONE_NUMBER)))
+ .orElse(null));
+
+ Response challenge = context.form()
+ .setAttribute(SupportPhonePages.FIELD_PHONE_NUMBER, userPhoneNumber)
+ .createForm("login-sms-otp-config.ftl");
+ context.challenge(challenge);
}
- }
- @Override
- public void close() {
- }
+ @Override
+ public void processAction(RequiredActionContext context) {
+ var session = context.getSession();
+ PhoneVerificationCodeProvider phoneVerificationCodeProvider = session
+ .getProvider(PhoneVerificationCodeProvider.class);
+ String phoneNumber = context.getHttpRequest().getDecodedFormParameters()
+ .getFirst(SupportPhonePages.FIELD_PHONE_NUMBER);
+ String code = context.getHttpRequest().getDecodedFormParameters()
+ .getFirst(SupportPhonePages.FIELD_VERIFICATION_CODE);
+ try {
+ phoneNumber = Utils.canonicalizePhoneNumber(context.getSession(), phoneNumber);
+ phoneVerificationCodeProvider.validateCode(context.getUser(), phoneNumber, code,
+ TokenCodeType.OTP);
+
+ PhoneOtpCredentialProvider ocp = (PhoneOtpCredentialProvider) context.getSession()
+ .getProvider(CredentialProvider.class,
+ PhoneOtpCredentialProviderFactory.PROVIDER_ID);
+ ocp.createCredential(context.getRealm(), context.getUser(),
+ PhoneOtpCredentialModel.create(phoneNumber, code,
+ Utils.getOtpExpires(context.getSession())));
+ context.getAuthenticationSession().setAuthNote(VERIFIED_PHONE_NUMBER, phoneNumber);
+ context.success();
+ } catch (BadRequestException e) {
+
+ Response challenge = context.form()
+ .setError(SupportPhonePages.Errors.NO_PROCESS.message())
+ .createForm("login-sms-otp-config.ftl");
+ context.challenge(challenge);
+
+ } catch (ForbiddenException e) {
+
+ Response challenge = context.form()
+ .setAttribute("phoneNumber", phoneNumber)
+ .setError(SupportPhonePages.Errors.NOT_MATCH.message())
+ .createForm("login-sms-otp-config.ftl");
+ context.challenge(challenge);
+ } catch (PhoneNumberInvalidException e) {
+ Response challenge = context.form()
+ .setError(e.getErrorType().message())
+ .createForm("login-sms-otp-config.ftl");
+ context.challenge(challenge);
+ }
+ }
+
+ @Override
+ public void close() {
+ }
}
diff --git a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/requiredactions/UpdatePhoneNumberRequiredAction.java b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/requiredactions/UpdatePhoneNumberRequiredAction.java
index 31c98fff..4d6225ea 100644
--- a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/requiredactions/UpdatePhoneNumberRequiredAction.java
+++ b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/authentication/requiredactions/UpdatePhoneNumberRequiredAction.java
@@ -4,14 +4,12 @@
import cc.coopersoft.keycloak.phone.authentication.forms.SupportPhonePages;
import cc.coopersoft.keycloak.phone.providers.exception.PhoneNumberInvalidException;
import cc.coopersoft.keycloak.phone.providers.spi.PhoneVerificationCodeProvider;
-import cc.coopersoft.keycloak.phone.providers.spi.PhoneProvider;
-import com.google.i18n.phonenumbers.NumberParseException;
import org.keycloak.authentication.RequiredActionContext;
import org.keycloak.authentication.RequiredActionProvider;
-import javax.ws.rs.BadRequestException;
-import javax.ws.rs.ForbiddenException;
-import javax.ws.rs.core.Response;
+import jakarta.ws.rs.BadRequestException;
+import jakarta.ws.rs.ForbiddenException;
+import jakarta.ws.rs.core.Response;
public class UpdatePhoneNumberRequiredAction implements RequiredActionProvider {
@@ -30,11 +28,14 @@ public void requiredActionChallenge(RequiredActionContext context) {
@Override
public void processAction(RequiredActionContext context) {
- PhoneVerificationCodeProvider phoneVerificationCodeProvider = context.getSession().getProvider(PhoneVerificationCodeProvider.class);
- String phoneNumber = context.getHttpRequest().getDecodedFormParameters().getFirst(SupportPhonePages.FIELD_PHONE_NUMBER);
- String code = context.getHttpRequest().getDecodedFormParameters().getFirst(SupportPhonePages.FIELD_VERIFICATION_CODE);
+ PhoneVerificationCodeProvider phoneVerificationCodeProvider = context.getSession()
+ .getProvider(PhoneVerificationCodeProvider.class);
+ String phoneNumber = context.getHttpRequest().getDecodedFormParameters()
+ .getFirst(SupportPhonePages.FIELD_PHONE_NUMBER);
+ String code = context.getHttpRequest().getDecodedFormParameters()
+ .getFirst(SupportPhonePages.FIELD_VERIFICATION_CODE);
try {
- phoneNumber = Utils.canonicalizePhoneNumber(context.getSession(),phoneNumber);
+ phoneNumber = Utils.canonicalizePhoneNumber(context.getSession(), phoneNumber);
phoneVerificationCodeProvider.validateCode(context.getUser(), phoneNumber, code);
context.success();
} catch (BadRequestException e) {
@@ -53,9 +54,9 @@ public void processAction(RequiredActionContext context) {
context.challenge(challenge);
} catch (PhoneNumberInvalidException e) {
Response challenge = context.form()
- .setAttribute("phoneNumber", phoneNumber)
- .setError(e.getErrorType().message())
- .createForm("login-update-phone-number.ftl");
+ .setAttribute("phoneNumber", phoneNumber)
+ .setError(e.getErrorType().message())
+ .createForm("login-update-phone-number.ftl");
context.challenge(challenge);
}
}
diff --git a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/credential/PhoneOtpCredentialModel.java b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/credential/PhoneOtpCredentialModel.java
index d5ba710e..f36a8d26 100644
--- a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/credential/PhoneOtpCredentialModel.java
+++ b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/credential/PhoneOtpCredentialModel.java
@@ -10,7 +10,7 @@
import org.keycloak.models.credential.dto.OTPSecretData;
import org.keycloak.util.JsonSerialization;
-import javax.validation.constraints.NotNull;
+import jakarta.validation.constraints.NotNull;
import java.io.IOException;
import java.util.Date;
import java.util.Optional;
@@ -26,39 +26,42 @@ public PhoneOtpCredentialModel(SmsOtpCredentialData credentialData, OTPSecretDat
this.secretData = secretData;
}
- private static Optional getOtpCredentialModel(@NotNull UserModel user){
+ private static Optional getOtpCredentialModel(@NotNull UserModel user) {
return user.credentialManager()
- .getStoredCredentialsByTypeStream(PhoneOtpCredentialModel.TYPE).findFirst();
+ .getStoredCredentialsByTypeStream(PhoneOtpCredentialModel.TYPE).findFirst();
}
- public static Optional getSmsOtpCredentialData(@NotNull UserModel user){
+ public static Optional getSmsOtpCredentialData(
+ @NotNull UserModel user) {
return getOtpCredentialModel(user)
- .map(credentialModel -> {
- try {
- return JsonSerialization.readValue(credentialModel.getCredentialData(), PhoneOtpCredentialModel.SmsOtpCredentialData.class);
- } catch (IOException e) {
- throw new IllegalArgumentException(e);
- }
- });
+ .map(credentialModel -> {
+ try {
+ return JsonSerialization.readValue(credentialModel.getCredentialData(),
+ PhoneOtpCredentialModel.SmsOtpCredentialData.class);
+ } catch (IOException e) {
+ throw new IllegalArgumentException(e);
+ }
+ });
}
public static void updateOtpCredential(@NotNull UserModel user,
- @NotNull PhoneOtpCredentialModel.SmsOtpCredentialData credentialData,
- String secretValue){
+ @NotNull PhoneOtpCredentialModel.SmsOtpCredentialData credentialData,
+ String secretValue) {
getOtpCredentialModel(user)
- .ifPresent(credential -> {
- try {
- credential.setCredentialData(JsonSerialization.writeValueAsString(credentialData));
- credential.setSecretData(JsonSerialization.writeValueAsString(new OTPSecretData(secretValue)));
- PhoneOtpCredentialModel credentialModel = PhoneOtpCredentialModel.createFromCredentialModel(credential);
- user.credentialManager().updateStoredCredential(credentialModel);
- }catch (IOException ioe) {
- throw new RuntimeException(ioe);
- }
- });
+ .ifPresent(credential -> {
+ try {
+ credential.setCredentialData(JsonSerialization.writeValueAsString(credentialData));
+ credential.setSecretData(JsonSerialization.writeValueAsString(new OTPSecretData(secretValue)));
+ PhoneOtpCredentialModel credentialModel = PhoneOtpCredentialModel
+ .createFromCredentialModel(credential);
+ user.credentialManager().updateStoredCredential(credentialModel);
+ } catch (IOException ioe) {
+ throw new RuntimeException(ioe);
+ }
+ });
}
- public static PhoneOtpCredentialModel create(String phoneNumber, String secretValue,int expires) {
+ public static PhoneOtpCredentialModel create(String phoneNumber, String secretValue, int expires) {
SmsOtpCredentialData credentialData = new SmsOtpCredentialData(phoneNumber, expires);
OTPSecretData secretData = new OTPSecretData(secretValue);
PhoneOtpCredentialModel credentialModel = new PhoneOtpCredentialModel(credentialData, secretData);
@@ -69,8 +72,10 @@ public static PhoneOtpCredentialModel create(String phoneNumber, String secretVa
public static PhoneOtpCredentialModel createFromCredentialModel(CredentialModel credentialModel) {
try {
- SmsOtpCredentialData credentialData = JsonSerialization.readValue(credentialModel.getCredentialData(), SmsOtpCredentialData.class);
- OTPSecretData secretData = JsonSerialization.readValue(credentialModel.getSecretData(), OTPSecretData.class);
+ SmsOtpCredentialData credentialData = JsonSerialization.readValue(credentialModel.getCredentialData(),
+ SmsOtpCredentialData.class);
+ OTPSecretData secretData = JsonSerialization.readValue(credentialModel.getSecretData(),
+ OTPSecretData.class);
PhoneOtpCredentialModel credential = new PhoneOtpCredentialModel(credentialData, secretData);
credential.setUserLabel(credentialModel.getUserLabel());
@@ -105,8 +110,6 @@ public OTPSecretData getOTPSecretData() {
return secretData;
}
-
-
@Getter
public static class SmsOtpCredentialData {
private final String phoneNumber;
@@ -116,17 +119,17 @@ public static class SmsOtpCredentialData {
private final int expires;
@JsonIgnore
- public boolean isSecretInvalid(){
- if (expires <= 0){
+ public boolean isSecretInvalid() {
+ if (expires <= 0) {
return true;
}
- return new Date().getTime() > expires * 1000L + secretCreate;
+ return new Date().getTime() > expires * 1000L + secretCreate;
}
@JsonCreator
-// @ConstructorProperties("phoneNumber")
- public SmsOtpCredentialData(@JsonProperty("phoneNumber")String phoneNumber,
- @JsonProperty("expires") int expires) {
+ // @ConstructorProperties("phoneNumber")
+ public SmsOtpCredentialData(@JsonProperty("phoneNumber") String phoneNumber,
+ @JsonProperty("expires") int expires) {
this.phoneNumber = phoneNumber;
this.secretCreate = new Date().getTime();
this.expires = expires;
diff --git a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/credential/PhoneOtpCredentialProviderFactory.java b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/credential/PhoneOtpCredentialProviderFactory.java
index 1166952f..bf002771 100644
--- a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/credential/PhoneOtpCredentialProviderFactory.java
+++ b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/credential/PhoneOtpCredentialProviderFactory.java
@@ -1,6 +1,5 @@
package cc.coopersoft.keycloak.phone.credential;
-import org.keycloak.credential.CredentialProvider;
import org.keycloak.credential.CredentialProviderFactory;
import org.keycloak.models.KeycloakSession;
@@ -9,7 +8,7 @@ public class PhoneOtpCredentialProviderFactory implements CredentialProviderFact
public final static String PROVIDER_ID = "sms-otp";
@Override
- public CredentialProvider create(KeycloakSession session) {
+ public PhoneOtpCredentialProvider create(KeycloakSession session) {
return new PhoneOtpCredentialProvider(session);
}
diff --git a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/providers/jpa/TokenCode.java b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/providers/jpa/TokenCode.java
index 8a25ab2f..804dc197 100644
--- a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/providers/jpa/TokenCode.java
+++ b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/providers/jpa/TokenCode.java
@@ -2,70 +2,63 @@
import lombok.Data;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.util.Date;
@Entity
@Data
@Table(name = "PHONE_MESSAGE_TOKEN_CODE")
@NamedQueries({
- @NamedQuery(
- name = "ongoingProcess",
- query = "FROM TokenCode t WHERE t.realmId = :realmId " +
- "AND t.phoneNumber = :phoneNumber " +
- "AND t.expiresAt >= :now AND t.type = :type"
- ),
- @NamedQuery(
- name = "processesSinceTarget",
- query = "SELECT COUNT(t) FROM TokenCode t WHERE t.realmId = :realmId " +
- "AND t.phoneNumber = :phoneNumber " +
- "AND t.createdAt >= :date AND t.type = :type"
- ),
- @NamedQuery(
- name = "processesSinceSource",
- query = "SELECT COUNT(t) FROM TokenCode t WHERE t.realmId = :realmId " +
- "AND t.ip = :addr " +
- "AND t.createdAt >= :date AND t.type = :type"
- )
+ @NamedQuery(name = "ongoingProcess", query = "FROM TokenCode t WHERE t.realmId = :realmId " +
+ "AND t.phoneNumber = :phoneNumber " +
+ "AND t.expiresAt >= :now AND t.type = :type"),
+ @NamedQuery(name = "processesSinceTarget", query = "SELECT COUNT(t) FROM TokenCode t WHERE t.realmId = :realmId "
+ +
+ "AND t.phoneNumber = :phoneNumber " +
+ "AND t.createdAt >= :date AND t.type = :type"),
+ @NamedQuery(name = "processesSinceSource", query = "SELECT COUNT(t) FROM TokenCode t WHERE t.realmId = :realmId "
+ +
+ "AND t.ip = :addr " +
+ "AND t.createdAt >= :date AND t.type = :type")
})
public class TokenCode {
- @Id
- @Column(name = "ID")
- private String id;
+ @Id
+ @Column(name = "ID")
+ private String id;
- @Column(name = "REALM_ID", nullable = false)
- private String realmId;
+ @Column(name = "REALM_ID", nullable = false)
+ private String realmId;
- @Column(name = "PHONE_NUMBER", nullable = false)
- private String phoneNumber;
+ @Column(name = "PHONE_NUMBER", nullable = false)
+ private String phoneNumber;
- @Column(name = "TYPE", nullable = false)
- private String type;
+ @Column(name = "TYPE", nullable = false)
+ private String type;
- @Column(name = "CODE", nullable = false)
- private String code;
+ @Column(name = "CODE", nullable = false)
+ private String code;
- @Temporal(TemporalType.TIMESTAMP)
- @Column(name = "CREATED_AT", nullable = false)
- private Date createdAt;
+ @Temporal(TemporalType.TIMESTAMP)
+ @Column(name = "CREATED_AT", nullable = false)
+ private Date createdAt;
- @Temporal(TemporalType.TIMESTAMP)
- @Column(name = "EXPIRES_AT", nullable = false)
- private Date expiresAt;
+ @Temporal(TemporalType.TIMESTAMP)
+ @Column(name = "EXPIRES_AT", nullable = false)
+ private Date expiresAt;
- @Column(name = "CONFIRMED", nullable = false)
- private Boolean confirmed;
+ @Column(name = "CONFIRMED", nullable = false)
+ private Boolean confirmed;
- @Column(name = "BY_WHOM", nullable = true)
- private String byWhom;
+ @Column(name = "BY_WHOM", nullable = true)
+ private String byWhom;
- @Column(name = "IP")
- private String ip;
+ @Column(name = "IP")
+ private String ip;
- @Column(name = "PORT")
- private Integer port;
+ @Column(name = "PORT")
+ private Integer port;
- @Column(name = "HOST")
- private String host;
+ @Column(name = "HOST")
+ private String host;
}
diff --git a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/providers/rest/SmsResource.java b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/providers/rest/SmsResource.java
index 90ecfd94..fa3dc6fe 100644
--- a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/providers/rest/SmsResource.java
+++ b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/providers/rest/SmsResource.java
@@ -3,7 +3,7 @@
import cc.coopersoft.keycloak.phone.providers.constants.TokenCodeType;
import org.keycloak.models.KeycloakSession;
-import javax.ws.rs.Path;
+import jakarta.ws.rs.Path;
public class SmsResource {
diff --git a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/providers/rest/SmsResourceProviderFactory.java b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/providers/rest/SmsResourceProviderFactory.java
index 7e870da6..69c1ec6d 100644
--- a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/providers/rest/SmsResourceProviderFactory.java
+++ b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/providers/rest/SmsResourceProviderFactory.java
@@ -1,17 +1,13 @@
package cc.coopersoft.keycloak.phone.providers.rest;
-import org.jboss.logging.Logger;
import org.keycloak.Config.Scope;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.services.resource.RealmResourceProvider;
import org.keycloak.services.resource.RealmResourceProviderFactory;
-
public class SmsResourceProviderFactory implements RealmResourceProviderFactory {
- private static final Logger logger = Logger.getLogger(SmsResourceProviderFactory.class);
-
@Override
public String getId() {
return "sms";
diff --git a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/providers/rest/TokenCodeResource.java b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/providers/rest/TokenCodeResource.java
index 551f79fe..f3c764c3 100644
--- a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/providers/rest/TokenCodeResource.java
+++ b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/providers/rest/TokenCodeResource.java
@@ -9,12 +9,12 @@
import org.keycloak.models.KeycloakSession;
import org.keycloak.services.validation.Validation;
-import javax.validation.constraints.NotBlank;
-import javax.ws.rs.*;
-import javax.ws.rs.core.Response;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.ws.rs.*;
+import jakarta.ws.rs.core.Response;
-import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
-import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE;
+import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON;
+import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON_TYPE;
public class TokenCodeResource {
@@ -27,26 +27,26 @@ public class TokenCodeResource {
this.tokenCodeType = tokenCodeType;
}
-
@GET
@NoCache
@Path("")
@Produces(APPLICATION_JSON)
public Response getTokenCode(@NotBlank @QueryParam("phoneNumber") String phoneNumber,
- @QueryParam("kind") String kind) {
+ @QueryParam("kind") String kind) {
- if (Validation.isBlank(phoneNumber)) throw new BadRequestException("Must supply a phone number");
+ if (Validation.isBlank(phoneNumber))
+ throw new BadRequestException("Must supply a phone number");
var phoneProvider = session.getProvider(PhoneProvider.class);
try {
- phoneNumber = Utils.canonicalizePhoneNumber(session,phoneNumber);
+ phoneNumber = Utils.canonicalizePhoneNumber(session, phoneNumber);
} catch (PhoneNumberInvalidException e) {
throw new BadRequestException("Phone number is invalid");
}
// everybody phones authenticator send AUTH code
- if( !TokenCodeType.REGISTRATION.equals(tokenCodeType) &&
+ if (!TokenCodeType.REGISTRATION.equals(tokenCodeType) &&
!TokenCodeType.AUTH.equals(tokenCodeType) &&
!TokenCodeType.VERIFY.equals(tokenCodeType) &&
Utils.findUserByPhone(session, session.getContext().getRealm(), phoneNumber).isEmpty()) {
diff --git a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/providers/rest/VerificationCodeResource.java b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/providers/rest/VerificationCodeResource.java
index 95aec5fc..3a98d77a 100644
--- a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/providers/rest/VerificationCodeResource.java
+++ b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/providers/rest/VerificationCodeResource.java
@@ -2,22 +2,20 @@
import cc.coopersoft.keycloak.phone.providers.constants.TokenCodeType;
import cc.coopersoft.keycloak.phone.providers.spi.PhoneVerificationCodeProvider;
-import org.jboss.logging.Logger;
+
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.UserModel;
import org.keycloak.services.managers.AppAuthManager;
import org.keycloak.services.managers.AuthenticationManager.AuthResult;
-import javax.ws.rs.*;
-import javax.ws.rs.core.Response;
+import jakarta.ws.rs.*;
+import jakarta.ws.rs.core.Response;
-import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON;
public class VerificationCodeResource extends TokenCodeResource {
- private static final Logger logger = Logger.getLogger(VerificationCodeResource.class);
-
private final AuthResult auth;
VerificationCodeResource(KeycloakSession session) {
@@ -34,11 +32,14 @@ private PhoneVerificationCodeProvider getTokenCodeService() {
@Path("")
@Produces(APPLICATION_JSON)
public Response checkVerificationCode(@QueryParam("phoneNumber") String phoneNumber,
- @QueryParam("code") String code) {
-
- if (auth == null) throw new NotAuthorizedException("Bearer");
- if (phoneNumber == null) throw new BadRequestException("Must inform a phone number");
- if (code == null) throw new BadRequestException("Must inform a token code");
+ @QueryParam("code") String code) {
+
+ if (auth == null)
+ throw new NotAuthorizedException("Bearer");
+ if (phoneNumber == null)
+ throw new BadRequestException("Must inform a phone number");
+ if (code == null)
+ throw new BadRequestException("Must inform a token code");
UserModel user = auth.getUser();
getTokenCodeService().validateCode(user, phoneNumber, code);
diff --git a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/providers/spi/PhoneSpi.java b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/providers/spi/PhoneSpi.java
index 1b8153f9..7a4343c8 100644
--- a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/providers/spi/PhoneSpi.java
+++ b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/providers/spi/PhoneSpi.java
@@ -1,7 +1,5 @@
package cc.coopersoft.keycloak.phone.providers.spi;
-import org.keycloak.provider.Provider;
-import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
public class PhoneSpi implements Spi {
@@ -17,12 +15,12 @@ public String getName() {
}
@Override
- public Class extends Provider> getProviderClass() {
+ public Class getProviderClass() {
return PhoneProvider.class;
}
@Override
- public Class extends ProviderFactory> getProviderFactoryClass() {
+ public Class getProviderFactoryClass() {
return PhoneProviderFactory.class;
}
}
diff --git a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/providers/spi/impl/DefaultPhoneProvider.java b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/providers/spi/impl/DefaultPhoneProvider.java
index 350fd97f..857caa55 100644
--- a/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/providers/spi/impl/DefaultPhoneProvider.java
+++ b/keycloak-phone-provider/src/main/java/cc/coopersoft/keycloak/phone/providers/spi/impl/DefaultPhoneProvider.java
@@ -12,8 +12,8 @@
import org.keycloak.models.KeycloakSession;
import org.keycloak.services.validation.Validation;
-import javax.ws.rs.ForbiddenException;
-import javax.ws.rs.ServiceUnavailableException;
+import jakarta.ws.rs.ForbiddenException;
+import jakarta.ws.rs.ServiceUnavailableException;
import java.time.Instant;
import java.util.Optional;
@@ -32,24 +32,23 @@ public class DefaultPhoneProvider implements PhoneProvider {
this.session = session;
this.config = config;
-
this.service = session.listProviderIds(MessageSenderService.class)
.stream().filter(s -> s.equals(config.get("service")))
.findFirst().orElse(
session.listProviderIds(MessageSenderService.class)
- .stream().findFirst().orElse(null)
- );
+ .stream().findFirst().orElse(null));
- if (Validation.isBlank(this.service)){
+ if (Validation.isBlank(this.service)) {
logger.error("Message sender service provider not found!");
}
if (Validation.isBlank(config.get("service")))
logger.warn("No message sender service provider specified! Default provider'" +
- this.service + "' will be used. You can use keycloak start param '--spi-phone-default-service' to specify a different one. ");
+ this.service
+ + "' will be used. You can use keycloak start param '--spi-phone-default-service' to specify a different one. ");
this.tokenExpiresIn = config.getInt("tokenExpiresIn", 60);
- this.targetHourMaximum = config.getInt("targetHourMaximum",3);
+ this.targetHourMaximum = config.getInt("targetHourMaximum", 3);
this.sourceHourMaximum = config.getInt("sourceHourMaximum", 10);
}
@@ -57,24 +56,23 @@ public class DefaultPhoneProvider implements PhoneProvider {
public void close() {
}
-
private PhoneVerificationCodeProvider getTokenCodeService() {
return session.getProvider(PhoneVerificationCodeProvider.class);
}
- private String getRealmName(){
+ private String getRealmName() {
return session.getContext().getRealm().getName();
}
- private Optional getStringConfigValue(String configName){
+ private Optional getStringConfigValue(String configName) {
return OptionalUtils.ofBlank(OptionalUtils.ofBlank(config.get(getRealmName() + "-" + configName))
- .orElse(config.get(configName)));
+ .orElse(config.get(configName)));
}
- private boolean getBooleanConfigValue(String configName, boolean defaultValue){
- Boolean result = config.getBoolean(getRealmName() + "-" + configName,null);
+ private boolean getBooleanConfigValue(String configName, boolean defaultValue) {
+ Boolean result = config.getBoolean(getRealmName() + "-" + configName, null);
if (result == null) {
- result = config.getBoolean(configName,defaultValue);
+ result = config.getBoolean(configName, defaultValue);
}
return result;
}
@@ -115,27 +113,28 @@ public Optional phoneNumberRegex() {
}
@Override
- public int sendTokenCode(String phoneNumber,String sourceAddr,TokenCodeType type, String kind){
+ public int sendTokenCode(String phoneNumber, String sourceAddr, TokenCodeType type, String kind) {
- logger.info("send code to:" + phoneNumber );
+ logger.info("send code to:" + phoneNumber);
- if (getTokenCodeService().isAbusing(phoneNumber, type,sourceAddr, sourceHourMaximum, targetHourMaximum)) {
+ if (getTokenCodeService().isAbusing(phoneNumber, type, sourceAddr, sourceHourMaximum, targetHourMaximum)) {
throw new ForbiddenException("You requested the maximum number of messages the last hour");
}
TokenCodeRepresentation ongoing = getTokenCodeService().ongoingProcess(phoneNumber, type);
if (ongoing != null) {
- logger.info(String.format("No need of sending a new %s code for %s",type.label, phoneNumber));
+ logger.info(String.format("No need of sending a new %s code for %s", type.label, phoneNumber));
return (int) (ongoing.getExpiresAt().getTime() - Instant.now().toEpochMilli()) / 1000;
}
TokenCodeRepresentation token = TokenCodeRepresentation.forPhoneNumber(phoneNumber);
try {
- session.getProvider(MessageSenderService.class, service).sendSmsMessage(type,phoneNumber,token.getCode(),tokenExpiresIn,kind);
+ session.getProvider(MessageSenderService.class, service).sendSmsMessage(type, phoneNumber, token.getCode(),
+ tokenExpiresIn, kind);
getTokenCodeService().persistCode(token, type, tokenExpiresIn);
- logger.info(String.format("Sent %s code to %s over %s",type.label, phoneNumber, service));
+ logger.info(String.format("Sent %s code to %s over %s", type.label, phoneNumber, service));
} catch (MessageSendException e) {
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 430f1571..eb747050 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
@@ -21,11 +21,11 @@
import org.keycloak.services.validation.Validation;
import org.keycloak.util.JsonSerialization;
-import javax.persistence.EntityManager;
-import javax.persistence.NoResultException;
-import javax.persistence.TemporalType;
-import javax.ws.rs.BadRequestException;
-import javax.ws.rs.ForbiddenException;
+import jakarta.persistence.EntityManager;
+import jakarta.persistence.NoResultException;
+import jakarta.persistence.TemporalType;
+import jakarta.ws.rs.BadRequestException;
+import jakarta.ws.rs.ForbiddenException;
import java.io.IOException;
import java.time.Instant;
import java.util.Date;
@@ -79,37 +79,37 @@ public TokenCodeRepresentation ongoingProcess(String phoneNumber, TokenCodeType
} catch (NoResultException e) {
return null;
} catch (PhoneNumberInvalidException e) {
- logger.warn("Invalid number: "+phoneNumber);
+ logger.warn("Invalid number: " + phoneNumber);
throw new BadRequestException("Phone number is invalid");
}
}
@Override
public boolean isAbusing(String phoneNumber, TokenCodeType tokenCodeType,
- String sourceAddr, int sourceHourMaximum, int targetHourMaximum) {
+ String sourceAddr, int sourceHourMaximum, int targetHourMaximum) {
Date oneHourAgo = new Date(System.currentTimeMillis() - TimeUnit.HOURS.toMillis(1));
- if (targetHourMaximum > 0){
+ if (targetHourMaximum > 0) {
long targetCount = (getEntityManager()
- .createNamedQuery("processesSinceTarget", Long.class)
- .setParameter("realmId", getRealm().getId())
- .setParameter("phoneNumber", phoneNumber)
- .setParameter("date", oneHourAgo, TemporalType.TIMESTAMP)
- .setParameter("type", tokenCodeType.name())
- .getSingleResult());
+ .createNamedQuery("processesSinceTarget", Long.class)
+ .setParameter("realmId", getRealm().getId())
+ .setParameter("phoneNumber", phoneNumber)
+ .setParameter("date", oneHourAgo, TemporalType.TIMESTAMP)
+ .setParameter("type", tokenCodeType.name())
+ .getSingleResult());
if (targetCount > targetHourMaximum)
return true;
}
- if (sourceHourMaximum > 0){
+ if (sourceHourMaximum > 0) {
long sourceCount = (getEntityManager()
- .createNamedQuery("processesSinceSource", Long.class)
- .setParameter("realmId", getRealm().getId())
- .setParameter("addr", sourceAddr)
- .setParameter("date", oneHourAgo, TemporalType.TIMESTAMP)
- .setParameter("type", tokenCodeType.name())
- .getSingleResult());
+ .createNamedQuery("processesSinceSource", Long.class)
+ .setParameter("realmId", getRealm().getId())
+ .setParameter("addr", sourceAddr)
+ .setParameter("date", oneHourAgo, TemporalType.TIMESTAMP)
+ .setParameter("type", tokenCodeType.name())
+ .getSingleResult());
if (sourceCount > sourceHourMaximum)
return true;
}
@@ -154,60 +154,63 @@ public void validateCode(UserModel user, String phoneNumber, String code, TokenC
if (tokenCode == null)
throw new BadRequestException(String.format("There is no valid ongoing %s process", tokenCodeType.label));
- if (!tokenCode.getCode().equals(code)) throw new ForbiddenException("Code does not match with expected value");
+ if (!tokenCode.getCode().equals(code))
+ throw new ForbiddenException("Code does not match with expected value");
logger.info(String.format("User %s correctly answered the %s code", user.getId(), tokenCodeType.label));
- tokenValidated(user,phoneNumber,tokenCode.getId(),TokenCodeType.OTP.equals(tokenCodeType));
+ tokenValidated(user, phoneNumber, tokenCode.getId(), TokenCodeType.OTP.equals(tokenCodeType));
if (TokenCodeType.OTP.equals(tokenCodeType))
- updateUserOTPCredential(user,phoneNumber,tokenCode.getCode());
+ updateUserOTPCredential(user, phoneNumber, tokenCode.getCode());
}
@Override
public void tokenValidated(UserModel user, String phoneNumber, String tokenCodeId, boolean isOTP) {
boolean updateUserPhoneNumber = !isOTP;
- if (isOTP){
+ if (isOTP) {
updateUserPhoneNumber = PhoneOtpCredentialModel.getSmsOtpCredentialData(user)
- .map(PhoneOtpCredentialModel.SmsOtpCredentialData::getPhoneNumber)
- .map(pn -> pn.equals(phoneNumber))
- .orElse(false);
+ .map(PhoneOtpCredentialModel.SmsOtpCredentialData::getPhoneNumber)
+ .map(pn -> pn.equals(phoneNumber))
+ .orElse(false);
}
-
- if (updateUserPhoneNumber){
- if (!Utils.isDuplicatePhoneAllowed(session)){
+ if (updateUserPhoneNumber) {
+ if (!Utils.isDuplicatePhoneAllowed(session)) {
session.users()
- .searchForUserByUserAttributeStream(session.getContext().getRealm(),"phoneNumber", phoneNumber)
- .filter(u -> !u.getId().equals(user.getId()))
- .forEach(u -> {
- logger.info(String.format("User %s also has phone number %s. Un-verifying.", u.getId(), phoneNumber));
- u.setSingleAttribute("phoneNumberVerified", "false");
-
- u.addRequiredAction(UpdatePhoneNumberRequiredAction.PROVIDER_ID);
-
- //remove otp Credentials
- u.credentialManager()
- .getStoredCredentialsByTypeStream(PhoneOtpCredentialModel.TYPE)
- .filter(c -> {
- try {
- PhoneOtpCredentialModel.SmsOtpCredentialData credentialData =
- JsonSerialization.readValue(c.getCredentialData(), PhoneOtpCredentialModel.SmsOtpCredentialData.class);
- if (Validation.isBlank(credentialData.getPhoneNumber())){
- return true;
- }
- return credentialData.getPhoneNumber().equals(user.getFirstAttribute("phoneNumber"));
- } catch (IOException e) {
- logger.warn("Unknown format Otp Credential", e);
- return true;
- }
- })
- .map(CredentialModel::getId)
- .collect(Collectors.toList())
- .forEach(id -> u.credentialManager().removeStoredCredentialById(id));
- });
+ .searchForUserByUserAttributeStream(session.getContext().getRealm(), "phoneNumber", phoneNumber)
+ .filter(u -> !u.getId().equals(user.getId()))
+ .forEach(u -> {
+ logger.info(String.format("User %s also has phone number %s. Un-verifying.", u.getId(),
+ phoneNumber));
+ u.setSingleAttribute("phoneNumberVerified", "false");
+
+ u.addRequiredAction(UpdatePhoneNumberRequiredAction.PROVIDER_ID);
+
+ // remove otp Credentials
+ u.credentialManager()
+ .getStoredCredentialsByTypeStream(PhoneOtpCredentialModel.TYPE)
+ .filter(c -> {
+ try {
+ PhoneOtpCredentialModel.SmsOtpCredentialData credentialData = JsonSerialization
+ .readValue(c.getCredentialData(),
+ PhoneOtpCredentialModel.SmsOtpCredentialData.class);
+ if (Validation.isBlank(credentialData.getPhoneNumber())) {
+ return true;
+ }
+ return credentialData.getPhoneNumber()
+ .equals(user.getFirstAttribute("phoneNumber"));
+ } catch (IOException e) {
+ logger.warn("Unknown format Otp Credential", e);
+ return true;
+ }
+ })
+ .map(CredentialModel::getId)
+ .collect(Collectors.toList())
+ .forEach(id -> u.credentialManager().removeStoredCredentialById(id));
+ });
}
user.setSingleAttribute("phoneNumberVerified", "true");
user.setSingleAttribute("phoneNumber", phoneNumber);
@@ -217,7 +220,6 @@ public void tokenValidated(UserModel user, String phoneNumber, String tokenCodeI
validateProcess(tokenCodeId, user);
-
}
@Override
@@ -228,18 +230,17 @@ public void validateProcess(String tokenCodeId, UserModel user) {
getEntityManager().persist(entity);
}
-
- private void updateUserOTPCredential(UserModel user, String phoneNumber, String code) {
+ private void updateUserOTPCredential(UserModel user, String phoneNumber, String code) {
user.removeRequiredAction(ConfigSmsOtpRequiredAction.PROVIDER_ID);
- PhoneOtpCredentialProvider ocp = (PhoneOtpCredentialProvider)
- session.getProvider(CredentialProvider.class, PhoneOtpCredentialProviderFactory.PROVIDER_ID);
+ PhoneOtpCredentialProvider ocp = (PhoneOtpCredentialProvider) session.getProvider(CredentialProvider.class,
+ PhoneOtpCredentialProviderFactory.PROVIDER_ID);
if (ocp.isConfiguredFor(getRealm(), user, PhoneOtpCredentialModel.TYPE)) {
- var credentialData = new PhoneOtpCredentialModel.SmsOtpCredentialData(phoneNumber,Utils.getOtpExpires(session));
- PhoneOtpCredentialModel.updateOtpCredential(user,credentialData,code);
+ var credentialData = new PhoneOtpCredentialModel.SmsOtpCredentialData(phoneNumber,
+ Utils.getOtpExpires(session));
+ PhoneOtpCredentialModel.updateOtpCredential(user, credentialData, code);
}
}
-
@Override
public void close() {
}
diff --git a/keycloak-sms-provider-aws-sns/pom.xml b/keycloak-sms-provider-aws-sns/pom.xml
index e56dd8f7..e165230e 100755
--- a/keycloak-sms-provider-aws-sns/pom.xml
+++ b/keycloak-sms-provider-aws-sns/pom.xml
@@ -30,7 +30,7 @@
com.amazonaws
aws-java-sdk-bom
- 1.12.419
+ 1.12.762
pom
import
diff --git a/keycloak-sms-provider-aws-sns/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/AwsSnsMessageSenderServiceProviderFactory.java b/keycloak-sms-provider-aws-sns/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/AwsSnsMessageSenderServiceProviderFactory.java
index 0ac57f98..a9e88fee 100755
--- a/keycloak-sms-provider-aws-sns/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/AwsSnsMessageSenderServiceProviderFactory.java
+++ b/keycloak-sms-provider-aws-sns/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/AwsSnsMessageSenderServiceProviderFactory.java
@@ -12,7 +12,7 @@ public class AwsSnsMessageSenderServiceProviderFactory implements MessageSenderS
@Override
public MessageSenderService create(KeycloakSession keycloakSession) {
- return new AwsSnsSmsSenderService(keycloakSession.getContext().getRealm().getDisplayName(), config);
+ return new AwsSnsSmsSenderService(keycloakSession, config);
}
@Override
diff --git a/keycloak-sms-provider-aws-sns/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/AwsSnsSmsSenderService.java b/keycloak-sms-provider-aws-sns/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/AwsSnsSmsSenderService.java
index 0ccbe155..4a317fb3 100755
--- a/keycloak-sms-provider-aws-sns/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/AwsSnsSmsSenderService.java
+++ b/keycloak-sms-provider-aws-sns/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/AwsSnsSmsSenderService.java
@@ -9,6 +9,7 @@
import com.amazonaws.services.sns.model.PublishResult;
import org.jboss.logging.Logger;
import org.keycloak.Config;
+import org.keycloak.models.KeycloakSession;
import java.util.HashMap;
import java.util.Map;
@@ -18,8 +19,8 @@ public class AwsSnsSmsSenderService extends FullSmsSenderAbstractService {
private static final Logger logger = Logger.getLogger(AwsSnsSmsSenderService.class);
private final Config.Scope config;
- public AwsSnsSmsSenderService(String realmDisplay, Config.Scope config) {
- super(realmDisplay);
+ public AwsSnsSmsSenderService(KeycloakSession session, Config.Scope config) {
+ super(session);
this.config = config;
}
@@ -49,13 +50,14 @@ public void sendMessage(String phoneNumber, String message) throws MessageSendEx
}
private static void sendSMSMessage(AmazonSNS snsClient, String message,
- String phoneNumber, Map smsAttributes) {
+ String phoneNumber, Map smsAttributes) {
PublishResult result = snsClient.publish(new PublishRequest()
.withMessage(message)
.withPhoneNumber(phoneNumber)
.withMessageAttributes(smsAttributes));
- logger.debug(String.format("Sent phone verification code via aws sns with message id %s", result.getMessageId()));
+ logger.debug(
+ String.format("Sent phone verification code via aws sns with message id %s", result.getMessageId()));
}
@Override
diff --git a/keycloak-sms-provider-bulksms/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/BulksmsSmsSenderServiceProvider.java b/keycloak-sms-provider-bulksms/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/BulksmsSmsSenderServiceProvider.java
index 11f9f073..de1af807 100644
--- a/keycloak-sms-provider-bulksms/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/BulksmsSmsSenderServiceProvider.java
+++ b/keycloak-sms-provider-bulksms/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/BulksmsSmsSenderServiceProvider.java
@@ -2,8 +2,6 @@
import cc.coopersoft.keycloak.phone.providers.exception.MessageSendException;
import cc.coopersoft.keycloak.phone.providers.spi.FullSmsSenderAbstractService;
-import org.apache.http.client.HttpClient;
-import org.apache.http.impl.client.HttpClients;
import org.jboss.logging.Logger;
import org.keycloak.broker.provider.util.SimpleHttp;
import org.keycloak.models.KeycloakSession;
@@ -28,7 +26,9 @@ public class BulksmsSmsSenderServiceProvider extends FullSmsSenderAbstractServic
private final String from;
private final String encoding;
private final String routingGroup;
+ private final KeycloakSession session;
+ @SuppressWarnings("unused") // fields used in json serialization
private static class BulksmsMessage {
public String from;
public String to;
@@ -55,12 +55,12 @@ public BulksmsMessage(String from, String to, String body, String encoding, Stri
this.from = config.get(CONFIG_FROM);
this.encoding = config.get(CONFIG_ENCODING);
this.routingGroup = config.get(CONFIG_ROUTING_GROUP);
+ this.session = session;
}
@Override
public void sendMessage(String phoneNumber, String message) throws MessageSendException {
- HttpClient httpclient = HttpClients.createDefault();
- SimpleHttp req = SimpleHttp.doPost(url, httpclient);
+ SimpleHttp req = SimpleHttp.doPost(url, session);
req.json(new BulksmsMessage[] {
new BulksmsMessage(this.from, phoneNumber, message, this.encoding, this.routingGroup) });
req.authBasic(this.username, this.password);
diff --git a/keycloak-sms-provider-cloopen/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/CloopenSmsSenderServiceProvider.java b/keycloak-sms-provider-cloopen/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/CloopenSmsSenderServiceProvider.java
index 2ea687d3..02c9ec50 100644
--- a/keycloak-sms-provider-cloopen/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/CloopenSmsSenderServiceProvider.java
+++ b/keycloak-sms-provider-cloopen/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/CloopenSmsSenderServiceProvider.java
@@ -15,10 +15,8 @@
public class CloopenSmsSenderServiceProvider implements MessageSenderService {
-
private static final String APP_ID_PARAM_NAME = "app";
- private static final String TEMPLATE_PARAM_NAME = "-template";
private static final Logger logger = Logger.getLogger(CloopenSmsSenderServiceProvider.class);
private final CCPRestSmsSDK client;
@@ -30,15 +28,14 @@ public CloopenSmsSenderServiceProvider(Config.Scope config, RealmModel realm) {
this.config = config;
this.realm = realm;
- //生产环境请求地址:app.cloopen.com
+ // 生产环境请求地址:app.cloopen.com
String serverIp = "app.cloopen.com";
- //请求端口
+ // 请求端口
String serverPort = "8883";
- //主账号,登陆云通讯网站后,可在控制台首页看到开发者主账号ACCOUNT SID和主账号令牌AUTH TOKEN
+ // 主账号,登陆云通讯网站后,可在控制台首页看到开发者主账号ACCOUNT SID和主账号令牌AUTH TOKEN
String accountSId = config.get("account");
String accountToken = config.get("token");
-
logger.info(String.format("cloopen account: %s ; accountToken: %s", accountSId, accountToken));
client = new CCPRestSmsSDK();
@@ -55,38 +52,44 @@ public void close() {
}
@Override
- public void sendSmsMessage(TokenCodeType type, String phoneNumber, String code, int expires, String kind) throws MessageSendException {
- //请使用管理控制台中已创建应用的APPID
+ public void sendSmsMessage(TokenCodeType type, String phoneNumber, String code, int expires, String kind)
+ throws MessageSendException {
+ // 请使用管理控制台中已创建应用的APPID
String appId = Optional.ofNullable(config.get(realm.getName().toLowerCase() + "-" + APP_ID_PARAM_NAME))
.orElse(config.get(APP_ID_PARAM_NAME));
client.setAppId(appId);
String kindName = OptionalUtils.ofBlank(kind).orElse(type.name().toLowerCase());
- String templateId = Optional.ofNullable(config.get(realm.getName().toLowerCase() + "-" + kindName + "-template"))
- .orElse(config.get(kindName + "-template"));
+ String templateId = Optional
+ .ofNullable(config.get(realm.getName().toLowerCase() + "-" + kindName + "-template"))
+ .orElse(config.get(kindName + "-template"));
logger.info(String.format("cloopen appId: %s ; templateId: %s", appId, templateId));
- String[] datas = {code, String.valueOf(expires / 60) };
-
-// String subAppend="1234"; //可选 扩展码,四位数字 0~9999
-// String reqId="fadfafas"; //可选 第三方自定义消息id,最大支持32位英文数字,同账号下同一自然天内不允许重复
- Map result = client.sendTemplateSMS(phoneNumber,templateId,datas);
- //Map result = client.sendTemplateSMS(phoneNumber,templateId,datas,subAppend,reqId);
- if("000000".equals(result.get("statusCode"))){
- //正常返回输出data包体信息(map)
- Map data = (Map) result.get("data");
+ String[] datas = { code, String.valueOf(expires / 60) };
+
+ // String subAppend="1234"; //可选 扩展码,四位数字 0~9999
+ // String reqId="fadfafas"; //可选 第三方自定义消息id,最大支持32位英文数字,同账号下同一自然天内不允许重复
+ Map result = client.sendTemplateSMS(phoneNumber, templateId, datas);
+ // Map result =
+ // client.sendTemplateSMS(phoneNumber,templateId,datas,subAppend,reqId);
+ if ("000000".equals(result.get("statusCode"))) {
+ // 正常返回输出data包体信息(map)
+ @SuppressWarnings("unchecked")
+ Map data = (Map) result.get("data");
logger.info("cloopen send message result: " + data.toString());
-// Set keySet = data.keySet();
-// for(String key:keySet){
-// Object object = data.get(key);
-// System.out.println(key +" = "+object);
-//
-// }
- }else{
- //异常返回输出错误码和错误信息
- throw new MessageSendException(500, result.get("statusCode").toString(), result.get("statusMsg").toString());
- //System.out.println("错误码=" + result.get("statusCode") +" 错误信息= "+result.get("statusMsg"));
+ // Set keySet = data.keySet();
+ // for(String key:keySet){
+ // Object object = data.get(key);
+ // System.out.println(key +" = "+object);
+ //
+ // }
+ } else {
+ // 异常返回输出错误码和错误信息
+ throw new MessageSendException(500, result.get("statusCode").toString(),
+ result.get("statusMsg").toString());
+ // System.out.println("错误码=" + result.get("statusCode") +" 错误信息=
+ // "+result.get("statusMsg"));
}
}
}
diff --git a/keycloak-sms-provider-dummy/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/DummyMessageSenderServiceProviderFactory.java b/keycloak-sms-provider-dummy/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/DummyMessageSenderServiceProviderFactory.java
index c4a8f2a3..3bc06e58 100644
--- a/keycloak-sms-provider-dummy/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/DummyMessageSenderServiceProviderFactory.java
+++ b/keycloak-sms-provider-dummy/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/DummyMessageSenderServiceProviderFactory.java
@@ -10,7 +10,7 @@ public class DummyMessageSenderServiceProviderFactory implements MessageSenderSe
@Override
public MessageSenderService create(KeycloakSession keycloakSession) {
- return new DummySmsSenderService(keycloakSession.getContext().getRealm().getDisplayName());
+ return new DummySmsSenderService(keycloakSession);
}
@Override
diff --git a/keycloak-sms-provider-dummy/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/DummySmsSenderService.java b/keycloak-sms-provider-dummy/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/DummySmsSenderService.java
index f40b0ebf..ba40cb32 100644
--- a/keycloak-sms-provider-dummy/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/DummySmsSenderService.java
+++ b/keycloak-sms-provider-dummy/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/DummySmsSenderService.java
@@ -3,6 +3,7 @@
import cc.coopersoft.keycloak.phone.providers.exception.MessageSendException;
import cc.coopersoft.keycloak.phone.providers.spi.FullSmsSenderAbstractService;
import org.jboss.logging.Logger;
+import org.keycloak.models.KeycloakSession;
import java.util.Random;
@@ -10,8 +11,8 @@ public class DummySmsSenderService extends FullSmsSenderAbstractService {
private static final Logger logger = Logger.getLogger(DummySmsSenderService.class);
- public DummySmsSenderService(String realmDisplay) {
- super(realmDisplay);
+ public DummySmsSenderService(KeycloakSession session) {
+ super(session);
}
@Override
diff --git a/keycloak-sms-provider-tencent/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/TencentSmsSenderServiceProvider.java b/keycloak-sms-provider-tencent/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/TencentSmsSenderServiceProvider.java
index 51f4107f..44f944ee 100644
--- a/keycloak-sms-provider-tencent/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/TencentSmsSenderServiceProvider.java
+++ b/keycloak-sms-provider-tencent/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/TencentSmsSenderServiceProvider.java
@@ -17,91 +17,98 @@
public class TencentSmsSenderServiceProvider implements MessageSenderService {
private static final String APP_ID_PARAM_NAME = "app";
- private static final String TEMPLATE_PARAM_NAME = "template";
private final Config.Scope config;
private final RealmModel realm;
private final SmsClient client;
- public TencentSmsSenderServiceProvider(Config.Scope config,RealmModel realm) {
+ public TencentSmsSenderServiceProvider(Config.Scope config, RealmModel realm) {
this.config = config;
this.realm = realm;
- /* 必要步骤:
+ /*
+ * 必要步骤:
* 实例化一个认证对象,入参需要传入腾讯云账户密钥对 secretId 和 secretKey
* 本示例采用从环境变量读取的方式,需要预先在环境变量中设置这两个值
* 您也可以直接在代码中写入密钥对,但需谨防泄露,不要将代码复制、上传或者分享给他人
- * CAM 密钥查询:https://console.cloud.tencent.com/cam/capi*/
+ * CAM 密钥查询:https://console.cloud.tencent.com/cam/capi
+ */
Credential cred = new Credential(config.get("secret"), config.get("key"));
-// // 实例化一个 http 选项,可选,无特殊需求时可以跳过
-// HttpProfile httpProfile = new HttpProfile();
-// // 设置代理
-// httpProfile.setProxyHost("host");
-// httpProfile.setProxyPort(port);
-// /* SDK 默认使用 POST 方法。
-// * 如需使用 GET 方法,可以在此处设置,但 GET 方法无法处理较大的请求 */
-// httpProfile.setReqMethod("POST");
-// /* SDK 有默认的超时时间,非必要请不要进行调整
-// * 如有需要请在代码中查阅以获取最新的默认值 */
-// httpProfile.setConnTimeout(60);
-// /* SDK 会自动指定域名,通常无需指定域名,但访问金融区的服务时必须手动指定域名
-// * 例如 SMS 的上海金融区域名为 sms.ap-shanghai-fsi.tencentcloudapi.com */
-// httpProfile.setEndpoint("sms.tencentcloudapi.com");
-
-// /* 非必要步骤:
-// * 实例化一个客户端配置对象,可以指定超时时间等配置 */
-// ClientProfile clientProfile = new ClientProfile();
-// /* SDK 默认用 TC3-HMAC-SHA256 进行签名
-// * 非必要请不要修改该字段 */
-// clientProfile.setSignMethod("HmacSHA256");
-// clientProfile.setHttpProfile(httpProfile);
- /* 实例化 SMS 的 client 对象
- * 第二个参数是地域信息,可以直接填写字符串 ap-guangzhou,或者引用预设的常量 */
-// SmsClient client = new SmsClient(cred, "",clientProfile);
-
- client = new SmsClient(cred, config.get("region","ap-guangzhou"));
+ // // 实例化一个 http 选项,可选,无特殊需求时可以跳过
+ // HttpProfile httpProfile = new HttpProfile();
+ // // 设置代理
+ // httpProfile.setProxyHost("host");
+ // httpProfile.setProxyPort(port);
+ // /* SDK 默认使用 POST 方法。
+ // * 如需使用 GET 方法,可以在此处设置,但 GET 方法无法处理较大的请求 */
+ // httpProfile.setReqMethod("POST");
+ // /* SDK 有默认的超时时间,非必要请不要进行调整
+ // * 如有需要请在代码中查阅以获取最新的默认值 */
+ // httpProfile.setConnTimeout(60);
+ // /* SDK 会自动指定域名,通常无需指定域名,但访问金融区的服务时必须手动指定域名
+ // * 例如 SMS 的上海金融区域名为 sms.ap-shanghai-fsi.tencentcloudapi.com */
+ // httpProfile.setEndpoint("sms.tencentcloudapi.com");
+
+ // /* 非必要步骤:
+ // * 实例化一个客户端配置对象,可以指定超时时间等配置 */
+ // ClientProfile clientProfile = new ClientProfile();
+ // /* SDK 默认用 TC3-HMAC-SHA256 进行签名
+ // * 非必要请不要修改该字段 */
+ // clientProfile.setSignMethod("HmacSHA256");
+ // clientProfile.setHttpProfile(httpProfile);
+ /*
+ * 实例化 SMS 的 client 对象
+ * 第二个参数是地域信息,可以直接填写字符串 ap-guangzhou,或者引用预设的常量
+ */
+ // SmsClient client = new SmsClient(cred, "",clientProfile);
+
+ client = new SmsClient(cred, config.get("region", "ap-guangzhou"));
}
-
@Override
- public void sendSmsMessage(TokenCodeType type, String phoneNumber, String code, int expires,String kind) throws MessageSendException {
+ public void sendSmsMessage(TokenCodeType type, String phoneNumber, String code, int expires, String kind)
+ throws MessageSendException {
try {
- /* 实例化一个请求对象,根据调用的接口和实际情况,可以进一步设置请求参数
+ /*
+ * 实例化一个请求对象,根据调用的接口和实际情况,可以进一步设置请求参数
* 您可以直接查询 SDK 源码确定接口有哪些属性可以设置
* 属性可能是基本类型,也可能引用了另一个数据结构
- * 推荐使用 IDE 进行开发,可以方便地跳转查阅各个接口和数据结构的文档说明 */
+ * 推荐使用 IDE 进行开发,可以方便地跳转查阅各个接口和数据结构的文档说明
+ */
SendSmsRequest req = new SendSmsRequest();
- /* 填充请求参数,这里 request 对象的成员变量即对应接口的入参
+ /*
+ * 填充请求参数,这里 request 对象的成员变量即对应接口的入参
* 您可以通过官网接口文档或跳转到 request 对象的定义处查看请求参数的定义
* 基本类型的设置:
* 帮助链接:
* 短信控制台:https://console.cloud.tencent.com/smsv2
- * sms helper:https://cloud.tencent.com/document/product/382/3773 */
+ * sms helper:https://cloud.tencent.com/document/product/382/3773
+ */
/* 短信应用 ID: 在 [短信控制台] 添加应用后生成的实际 SDKAppID,例如1400006666 */
String appId = Optional.ofNullable(config.get(realm.getName().toLowerCase() + "-" + APP_ID_PARAM_NAME))
- .orElse(config.get(APP_ID_PARAM_NAME));
+ .orElse(config.get(APP_ID_PARAM_NAME));
req.setSmsSdkAppid(appId);
/* 短信签名内容: 使用 UTF-8 编码,必须填写已审核通过的签名,可登录 [短信控制台] 查看签名信息 */
String sign = realm.getDisplayName();
req.setSign(sign);
/* 国际/港澳台短信 senderid: 国内短信填空,默认未开通,如需开通请联系 [sms helper] */
-// String senderid = "xxx";
-// req.setSenderId(senderid);
+ // String senderid = "xxx";
+ // req.setSenderId(senderid);
/* 用户的 session 内容: 可以携带用户侧 ID 等上下文信息,server 会原样返回 */
-// String session = "xxx";
-// req.setSessionContext(session);
+ // String session = "xxx";
+ // req.setSessionContext(session);
/* 短信码号扩展号: 默认未开通,如需开通请联系 [sms helper] */
-// String extendcode = "xxx";
-// req.setExtendCode(extendcode);
+ // String extendcode = "xxx";
+ // req.setExtendCode(extendcode);
/* 模板 ID: 必须填写已审核通过的模板 ID,可登录 [短信控制台] 查看模板 ID */
String kindName = OptionalUtils.ofBlank(kind).orElse(type.name().toLowerCase());
@@ -109,17 +116,21 @@ public void sendSmsMessage(TokenCodeType type, String phoneNumber, String code,
.orElse(config.get(kindName + "-template"));
req.setTemplateID(templateId);
- /* 下发手机号码,采用 e.164 标准,+[国家或地区码][手机号]
- * 例如+8613711112222, 其中前面有一个+号 ,86为国家码,13711112222为手机号,最多不要超过200个手机号*/
- String[] phoneNumbers = {phoneNumber};
+ /*
+ * 下发手机号码,采用 e.164 标准,+[国家或地区码][手机号]
+ * 例如+8613711112222, 其中前面有一个+号 ,86为国家码,13711112222为手机号,最多不要超过200个手机号
+ */
+ String[] phoneNumbers = { phoneNumber };
req.setPhoneNumberSet(phoneNumbers);
- /* 模板参数: 若无模板参数,则设置为空*/
- String[] templateParams = {code, String.valueOf(expires / 60) };
+ /* 模板参数: 若无模板参数,则设置为空 */
+ String[] templateParams = { code, String.valueOf(expires / 60) };
req.setTemplateParamSet(templateParams);
- /* 通过 client 对象调用 SendSms 方法发起请求。注意请求方法名与请求对象是对应的
- * 返回的 res 是一个 SendSmsResponse 类的实例,与请求对象对应 */
+ /*
+ * 通过 client 对象调用 SendSms 方法发起请求。注意请求方法名与请求对象是对应的
+ * 返回的 res 是一个 SendSmsResponse 类的实例,与请求对象对应
+ */
SendSmsResponse res = client.SendSms(req);
// 输出 JSON 格式的字符串回包
diff --git a/keycloak-sms-provider-totalvoice/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/TotalVoiceMessageSenderServiceProviderFactory.java b/keycloak-sms-provider-totalvoice/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/TotalVoiceMessageSenderServiceProviderFactory.java
index d5a6dc85..f7f8b97e 100644
--- a/keycloak-sms-provider-totalvoice/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/TotalVoiceMessageSenderServiceProviderFactory.java
+++ b/keycloak-sms-provider-totalvoice/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/TotalVoiceMessageSenderServiceProviderFactory.java
@@ -12,7 +12,7 @@ public class TotalVoiceMessageSenderServiceProviderFactory implements MessageSen
@Override
public MessageSenderService create(KeycloakSession session) {
- return new TotalVoiceSmsSenderServiceProvider(config, session.getContext().getRealm().getDisplayName());
+ return new TotalVoiceSmsSenderServiceProvider(session, config);
}
@Override
diff --git a/keycloak-sms-provider-totalvoice/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/TotalVoiceSmsSenderServiceProvider.java b/keycloak-sms-provider-totalvoice/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/TotalVoiceSmsSenderServiceProvider.java
index 3319fce9..6d98487e 100644
--- a/keycloak-sms-provider-totalvoice/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/TotalVoiceSmsSenderServiceProvider.java
+++ b/keycloak-sms-provider-totalvoice/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/TotalVoiceSmsSenderServiceProvider.java
@@ -6,14 +6,15 @@
import cc.coopersoft.keycloak.phone.providers.exception.MessageSendException;
import org.json.JSONObject;
import org.keycloak.Config.Scope;
+import org.keycloak.models.KeycloakSession;
public class TotalVoiceSmsSenderServiceProvider extends FullSmsSenderAbstractService {
private final TotalVoiceClient client;
private final Sms smsClient;
- TotalVoiceSmsSenderServiceProvider(Scope config, String realmDisplay) {
- super(realmDisplay);
+ TotalVoiceSmsSenderServiceProvider(KeycloakSession session, Scope config) {
+ super(session);
this.client = new TotalVoiceClient(config.get("token"));
this.smsClient = new Sms(client);
}
diff --git a/keycloak-sms-provider-twilio/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/TwilioMessageSenderServiceProviderFactory.java b/keycloak-sms-provider-twilio/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/TwilioMessageSenderServiceProviderFactory.java
index 2db25788..50a61104 100644
--- a/keycloak-sms-provider-twilio/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/TwilioMessageSenderServiceProviderFactory.java
+++ b/keycloak-sms-provider-twilio/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/TwilioMessageSenderServiceProviderFactory.java
@@ -12,7 +12,7 @@ public class TwilioMessageSenderServiceProviderFactory implements MessageSenderS
@Override
public MessageSenderService create(KeycloakSession session) {
- return new TwilioSmsSenderServiceProvider(config,session.getContext().getRealm().getDisplayName());
+ return new TwilioSmsSenderServiceProvider(session, config);
}
@Override
diff --git a/keycloak-sms-provider-twilio/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/TwilioSmsSenderServiceProvider.java b/keycloak-sms-provider-twilio/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/TwilioSmsSenderServiceProvider.java
index 42af6c29..7ac5de36 100644
--- a/keycloak-sms-provider-twilio/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/TwilioSmsSenderServiceProvider.java
+++ b/keycloak-sms-provider-twilio/src/main/java/cc/coopersoft/keycloak/phone/providers/sender/TwilioSmsSenderServiceProvider.java
@@ -7,14 +7,15 @@
import cc.coopersoft.keycloak.phone.providers.spi.FullSmsSenderAbstractService;
import org.jboss.logging.Logger;
import org.keycloak.Config.Scope;
+import org.keycloak.models.KeycloakSession;
public class TwilioSmsSenderServiceProvider extends FullSmsSenderAbstractService {
private static final Logger logger = Logger.getLogger(TwilioSmsSenderServiceProvider.class);
private final String twilioPhoneNumber;
- TwilioSmsSenderServiceProvider(Scope config, String realmDisplay) {
- super(realmDisplay);
+ TwilioSmsSenderServiceProvider(KeycloakSession session, Scope config) {
+ super(session);
Twilio.init(config.get("account"), config.get("token"));
this.twilioPhoneNumber = config.get("number");
diff --git a/keycloak-sms-provider-twofactorapi/pom.xml b/keycloak-sms-provider-twofactorapi/pom.xml
index d9892278..52e3d292 100644
--- a/keycloak-sms-provider-twofactorapi/pom.xml
+++ b/keycloak-sms-provider-twofactorapi/pom.xml
@@ -30,6 +30,11 @@
okhttp
4.10.0
+
+ jakarta.annotation
+ jakarta.annotation-api
+ 3.0.0
+
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
index 52b085dd..6dc13142 100644
--- 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
@@ -12,7 +12,7 @@ public class TwoFactorMessageSenderServiceProviderFactory implements MessageSend
@Override
public MessageSenderService create(KeycloakSession session) {
- return new TwoFactorSmsSenderServiceProvider(config,session.getContext().getRealm().getDisplayName());
+ return new TwoFactorSmsSenderServiceProvider(session, config);
}
@Override
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
index 713686cc..c04b171a 100644
--- 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
@@ -7,8 +7,9 @@
import okhttp3.Response;
import org.jboss.logging.Logger;
import org.keycloak.Config.Scope;
+import org.keycloak.models.KeycloakSession;
-import javax.annotation.PostConstruct;
+import jakarta.annotation.PostConstruct;
public class TwoFactorSmsSenderServiceProvider extends FullSmsSenderAbstractService {
@@ -23,8 +24,8 @@ public void doSetUp() {
.build();
}
- TwoFactorSmsSenderServiceProvider(Scope config, String realmDisplay) {
- super(realmDisplay);
+ TwoFactorSmsSenderServiceProvider(KeycloakSession session, Scope config) {
+ super(session);
this.twoFactorApiKey = config.get("twoFactorApiKey");
}
diff --git a/pom.xml b/pom.xml
index 64be29f9..7bca9934 100644
--- a/pom.xml
+++ b/pom.xml
@@ -43,13 +43,13 @@
UTF-8
- 17
- 17
- 17
- 21.0.2
+ 21
+ 21
+ 21
+ 25.0.2
+ 1.18.34
-
github
@@ -77,7 +77,7 @@
org.projectlombok
lombok
- 1.18.26
+ ${version.lombok}
provided
@@ -104,16 +104,14 @@
${version.keycloak}
provided
-
-
org.codehaus.mojo
versions-maven-plugin
- 2.10.0
+ 2.17.1
false
@@ -121,6 +119,7 @@
false
maven-resources-plugin
+ 3.3.1
copy-resources