Skip to content

Trunk-6663: Implement argon 2 id password encoder with configurable work factors#6301

Open
EDSONZ-WASSWA wants to merge 8 commits into
openmrs:2.8.xfrom
EDSONZ-WASSWA:TRUNK-6663-implement-argon-2-id-password-encoder-with-configurable-work-factors
Open

Trunk-6663: Implement argon 2 id password encoder with configurable work factors#6301
EDSONZ-WASSWA wants to merge 8 commits into
openmrs:2.8.xfrom
EDSONZ-WASSWA:TRUNK-6663-implement-argon-2-id-password-encoder-with-configurable-work-factors

Conversation

@EDSONZ-WASSWA

@EDSONZ-WASSWA EDSONZ-WASSWA commented Jul 13, 2026

Copy link
Copy Markdown

Summary

This PR implements Argon2id as the default password encoder for OpenMRS while preserving backward compatibility with existing password hashes.

Argon2 work factors (memory, parallelism, iterations, hash length, and salt length) are configurable through Global Properties with documented defaults. The encoder automatically refreshes when configuration changes, allowing updated settings to take effect without requiring a server restart.

The implementation validates all configurable work factor values, ensuring they are positive and falling back to safe defaults with warning logs when invalid values are configured. Existing SHA-512 and SHA-1 password hashes continue to verify correctly, while newly encoded passwords use Argon2id.

To preserve existing deterministic hashing behaviour outside password storage, a dedicated Security.encodeStringSHA512() helper was introduced. Existing consumers such as activation keys and secret-answer hashing continue using SHA-512, preventing regressions in features that rely on deterministic hashes while allowing password hashing to transition to Argon2id.

The configuration-loading logic was also updated to safely fall back to the default Argon2 parameters when the application service layer is not yet available (for example during startup or database upgrades), while automatically using Global Property values once the service layer has been initialized.

Changes

  • Added the required spring-security-crypto and argon2-jvm dependencies.
  • Implemented Argon2id password encoding in Security.encodeString().
  • Added configurable Argon2 Global Properties and their Javadocs.
  • Updated Security.hashMatches() to verify Argon2 hashes while maintaining compatibility with legacy hashes.
  • Introduced Security.encodeStringSHA512() for deterministic hashing use cases.
  • Added validation for configurable Argon2 work factors, with safe fallback to documented defaults.
  • Added automatic encoder refresh when Argon2 configuration changes.
  • Updated deterministic hashing consumers (activation keys and secret-answer hashing) to continue using SHA-512.
  • Made configuration loading resilient during application startup and database upgrade scenarios.
  • Added unit tests covering Argon2 encoding, configuration updates, and invalid configuration values.

Backward Compatibility

  • Existing SHA-512 and SHA-1 password hashes continue to verify correctly through the existing password verification mechanisms.
  • New password hashes are encoded using Argon2id.
  • Existing deterministic hashing consumers (such as activation keys and secret answers) continue using SHA-512 to preserve their current behaviour.
  • Invalid or unavailable Argon2 configuration safely falls back to documented defaults.

Issue I worked on

https://openmrs.atlassian.net/issues?filter=-1&selectedIssue=TRUNK-6663

Checklist: I completed these to help reviewers :)

  • My IDE is configured to follow the code style of this project.

    No? Unsure? -> configure your IDE, format the code and add the changes with git add . && git commit --amend

  • I have added tests to cover my changes. (If you refactored
    existing code that was well tested you do not have to add tests)

    No? -> write tests and add them to this commit git add . && git commit --amend

  • I ran ./mvnw clean package right before creating this pull request and
    added all formatting changes to my commit.

    No? -> execute above command

  • All new and existing tests passed.

    No? -> figure out why and add the fix to your commit. It is your responsibility to make sure your code works.

  • My pull request is based on the latest changes of the master branch.

    No? Unsure? -> execute command git pull --rebase upstream master

@jwnasambu jwnasambu left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, @EDSONZ-WASSWA, for the progress! Kindly note that we're pushing changes to the 2.8.x branch, not the master branch. Please follow these steps to change the base branch of your pull request on github please.

Open your pull request on GitHub.
Near the top of the PR, click Edit.
You will see something like base: master and compare: your-branch.
Change the base dropdown from master to 2.8.x.
Review any changes GitHub shows, then click Save or Update pull request.

@EDSONZ-WASSWA
EDSONZ-WASSWA marked this pull request as draft July 13, 2026 19:57
@EDSONZ-WASSWA
EDSONZ-WASSWA changed the base branch from master to 2.8.x July 13, 2026 19:59
@EDSONZ-WASSWA
EDSONZ-WASSWA marked this pull request as ready for review July 13, 2026 20:00
@EDSONZ-WASSWA
EDSONZ-WASSWA requested a review from jwnasambu July 13, 2026 20:27
@jwnasambu

Copy link
Copy Markdown
Contributor

@claude review

@EDSONZ-WASSWA

Copy link
Copy Markdown
Author

@dkayiwa please have a look at this PR

Comment thread api/src/main/java/org/openmrs/util/Security.java
Comment thread api/src/main/java/org/openmrs/util/Security.java Outdated
Comment thread api/src/main/java/org/openmrs/util/OpenmrsConstants.java
Comment thread api/src/test/java/org/openmrs/util/SecurityTest.java Outdated
…onfigurable-work-factors' of https://github.com/EDSONZ-WASSWA/openmrs-core into TRUNK-6663-implement-argon-2-id-password-encoder-with-configurable-work-factors
@EDSONZ-WASSWA
EDSONZ-WASSWA requested a review from jwnasambu July 14, 2026 14:08
@jwnasambu

Copy link
Copy Markdown
Contributor

@claude review

@github-actions

Copy link
Copy Markdown

This needs fixing before merge — it's the current, reproducible CI failure on this exact commit (ec9c38f5, ubuntu-8 job): https://github.com/openmrs/openmrs-core/actions/runs/29339311381/job/87106555567

api/src/test/java/org/openmrs/api/db/UserDAOTest.java isn't touched by this PR, but it now fails because of it. In four tests it computes hashedSecretAnswer with Security.encodeString(SECRET_ANSWER + lc.getSalt()), which this PR changed to Argon2id (a fresh random salt per call). Production's changeQuestionAnswer/isSecretAnswer hash the secret answer with Security.encodeStringSHA512(...) instead (to stay deterministic), so the two can never .equals() each other. All four fail with exactly this mismatch (expected an $argon2id$... hash, got the SHA-512 hex string actually stored):

  • updateUserPassword_shouldNotOverwriteUserSecretQuestionOrAnswer (UserDAOTest.java:115)
  • saveUser_shouldNotOverwriteUserSecretQuestionOrAnswer (UserDAOTest.java:129)
  • changePassword_shouldNotOverwriteUserSecretQuestionOrAnswer (UserDAOTest.java:166)
  • changeHashedPassword_shouldNotOverwriteUserSecretQuestionOrAnswer (UserDAOTest.java:181)

Switching all four to Security.encodeStringSHA512(SECRET_ANSWER + lc.getSalt()) should fix them.

@jwnasambu jwnasambu left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kindly on this file api/src/test/java/org/openmrs/util/SecurityTest.java all the 3 new test methods use setAccessible(true) to manipulate static fields and ServiceContext. These tests are fragile any internal refactor breaks them. Consider adding a package private static void resetEncoder() method in Security.java specifically for testing, or use a test-specific subclass/factory pattern.

Comment thread api/src/main/java/org/openmrs/util/OpenmrsConstants.java Outdated
@EDSONZ-WASSWA
EDSONZ-WASSWA requested a review from jwnasambu July 16, 2026 08:46
@jwnasambu

Copy link
Copy Markdown
Contributor

@claude review

Comment thread api/src/main/java/org/openmrs/util/Security.java
Comment thread api/src/main/java/org/openmrs/util/Security.java
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants