Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions keycloak/sms-provider/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -121,5 +121,10 @@
<!--<scope>test</scope> -->
</dependency>

<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>5.5.0</version>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -670,6 +673,9 @@ public boolean validatePassword(AuthenticationFlowContext context, UserModel use
List<CredentialInput> 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;
Expand All @@ -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<CredentialModel> 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;
}
}