Answer
For an R Shiny app that must run on Linux, Windows and macOS without compiled dependencies, use bcrypt with a work factor of 10 (or the lowest value that keeps latency under 200 ms).
Confirmed facts
- The
bcrypt CRAN package ships pre‑compiled binaries for all three platforms; bcrypt::hashpw() and bcrypt::checkpw() are pure‑R calls.
- A bcrypt hash string encodes the salt, cost factor and the hash, so no separate salt column is needed.
- Increasing the cost factor later only requires re‑hashing existing passwords; the stored format stays compatible.
openssl::sha256() (or digest::sha256()) is available in base R, but the salt must be generated and stored separately (e.g., as a hex string in its own column).
- SHA‑256 is deliberately fast; it provides no adaptive work factor, so future increases in attacker computing power would require a migration to a slower KDF.
- Typical bcrypt cost = 10 on a modern CPU yields login latency well under 200 ms; SHA‑256 latency is negligible (<1 ms).
Likely explanation (upgrade path & salt handling)
Because bcrypt embeds its parameters, upgrading the work factor is a straightforward re‑hash operation with no schema change. With salted SHA‑256 you would need to add a version flag or a new column to distinguish old from new hashes, and you would also need to replace the salt when changing the algorithm.
Store the bcrypt hash as a single VARCHAR column. If you ever need to record the cost factor separately (e.g., for auditing), you can extract it from the hash string, but no extra column is required for normal operation.
Verification steps to confirm identical behaviour across OS
On each target OS, run the following R snippet to generate a hash for a known password and verify it:
library(bcrypt)
pw <- "Secret123!"
hash <- bcrypt::hashpw(pw, cost = 10)
ok <- bcrypt::checkpw(pw, hash)
print(paste("Hash:", hash))
print(paste("Check:", ok))
- Compare the resulting hash strings byte‑by‑byte; they must be identical on Linux, Windows and macOS.
- Measure login latency with a realistic load (e.g., 10 concurrent
checkpw calls) using system.time() or microbenchmark. Ensure the median stays below 200 ms.
- If latency exceeds the limit, repeat the test with a lower cost (e.g., 8) and record the new latency.
Missing diagnostic detail
What is the observed median login latency for bcrypt cost = 10 on your target hardware (especially the oldest CPU you expect to run the Shiny app)? If the latency is >200 ms, the recommendation may shift to a lower bcrypt cost or to an alternative KDF that meets the latency bound.