diff --git a/keycloak/sms-provider/pom.xml b/keycloak/sms-provider/pom.xml
index 5a62df19..0e446ad9 100644
--- a/keycloak/sms-provider/pom.xml
+++ b/keycloak/sms-provider/pom.xml
@@ -121,5 +121,10 @@
+
+ org.springframework.security
+ spring-security-core
+ 5.5.0
+
diff --git a/keycloak/sms-provider/src/main/java/org/sunbird/keycloak/login/PasswordAndOtpAuthenticator.java b/keycloak/sms-provider/src/main/java/org/sunbird/keycloak/login/PasswordAndOtpAuthenticator.java
index 132073a6..c7168fdc 100644
--- a/keycloak/sms-provider/src/main/java/org/sunbird/keycloak/login/PasswordAndOtpAuthenticator.java
+++ b/keycloak/sms-provider/src/main/java/org/sunbird/keycloak/login/PasswordAndOtpAuthenticator.java
@@ -45,6 +45,7 @@
import org.keycloak.services.ServicesLogger;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.messages.Messages;
+import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder;
import org.sunbird.keycloak.resetcredential.sms.KeycloakSmsAuthenticatorConstants;
import org.sunbird.keycloak.resetcredential.sms.KeycloakSmsAuthenticatorUtil;
import org.sunbird.keycloak.utils.Constants;
@@ -61,6 +62,8 @@ public class PasswordAndOtpAuthenticator extends AbstractUsernameFormAuthenticat
Logger logger = Logger.getLogger(PasswordAndOtpAuthenticator.class);
private static final SecureRandom random = new SecureRandom();
+ private Pbkdf2PasswordEncoder passwordEncoder = new Pbkdf2PasswordEncoder();
+
private enum CODE_STATUS {
VALID, INVALID, EXPIRED
@@ -670,6 +673,9 @@ public boolean validatePassword(AuthenticationFlowContext context, UserModel use
List credentials = new LinkedList<>();
credentials.add(UserCredentialModel.password(decryptedPassword));
+ boolean isValid = validateHashedPassword(context, user, decryptedPassword);
+ logger.info(String.format("PasswordAndOtpAuthenticator::validateHashedPassword returns : %s", isValid));
+
if (decryptedPassword != null && !decryptedPassword.isEmpty()
&& context.getSession().userCredentialManager().isValid(context.getRealm(), user, credentials)) {
return true;
@@ -695,4 +701,43 @@ private String decryptPassword(String encryptedPassword, String secretKey, Strin
throw new RuntimeException("Error while decrypting password", e);
}
}
+
+ private boolean validateHashedPassword(AuthenticationFlowContext context, UserModel user,
+ String clientHashedPassword) {
+ try {
+
+ // Fetch the stored password credential
+ List credentials = context.getSession().userCredentialManager()
+ .getStoredCredentialsByType(context.getRealm(), user, CredentialModel.PASSWORD);
+
+ if (!credentials.isEmpty()) {
+ CredentialModel storedCredential = credentials.get(0);
+ logger.info(String.format(
+ "PasswordAndOtpAuthenticator::validateHashedPassword storedPasswordHash from list : %s",
+ storedCredential.getValue()));
+ } else {
+ logger.info(String.format("PasswordAndOtpAuthenticator::validateHashedPassword null value from type"));
+ }
+
+ // Fetch the stored password from Keycloak
+ CredentialModel storedCredential = context.getSession().userCredentialManager()
+ .getStoredCredentialById(context.getRealm(), user, CredentialModel.PASSWORD);
+
+ // The password stored in Keycloak is hashed with PBKDF2
+ if (storedCredential != null) {
+ String storedPasswordHash = storedCredential.getValue();
+ logger.info(String.format("PasswordAndOtpAuthenticator::validateHashedPassword storedPasswordHash : %s",
+ storedPasswordHash));
+ // Compare the PBKDF2-hashed password from client with Keycloak's stored PBKDF2
+ // hash
+ return passwordEncoder.matches(clientHashedPassword, storedPasswordHash);
+ } else {
+ logger.info(String.format("PasswordAndOtpAuthenticator::validateHashedPassword null value from id"));
+ }
+
+ } catch (Exception e) {
+ logger.error("Failed to validateHashedPassword. Exception: ", e);
+ }
+ return false;
+ }
}