Hint 1: Pick the parameters once
Create a PasswordHasher with Argon2id at or above m=19456, t=2, p=1. verify() reads the parameters from the stored hash, so it checks old hashes too; your instance’s parameters define the policy for rehashing.
Hint 1 unlocks after your first failed run.
Hint 2: Treat the exception, and rehash
verify() raises VerifyMismatchError on a wrong password and InvalidHashError on an unusable string — both mean authenticated False. After a success, use check_needs_rehash to decide whether to return a fresh hash.
Hint 2 unlocks after 2 failed runs.
Reveal the reference patch and explanation
from argon2 import PasswordHasher
from argon2.exceptions import InvalidHashError, VerificationError, VerifyMismatchError
def check_login(stored_hash, password):
hasher = PasswordHasher(time_cost=2, memory_cost=19456, parallelism=1)
try:
hasher.verify(stored_hash, password)
except (VerifyMismatchError, VerificationError, InvalidHashError):
return {"authenticated": False, "new_hash": None}
new_hash = hasher.hash(password) if hasher.check_needs_rehash(stored_hash) else None
return {"authenticated": True, "new_hash": new_hash}A PasswordHasher set to Argon2id m=19456, t=2, p=1 verifies the stored hash. A wrong password raises VerifyMismatchError, a bcrypt hash and a malformed string raise InvalidHashError, and all return {"authenticated": False, "new_hash": None}. On success, check_needs_rehash reports true for a hash whose parameters are below the instance’s (the m=512 hash) or whose variant differs (an Argon2i hash), so the function returns a fresh Argon2id hash at the policy minimum; a hash already at the policy strength needs no rehash and new_hash is None. argon2-cffi generates a random 16-byte salt per hash, so no salt handling is needed. The OWASP minimum (Argon2id m=19456, t=2, p=1) and the argon2-cffi defaults (RFC 9106 low-memory: m=65536, t=3, p=4) were both confirmed on 2026-10-11; either is acceptable, and parameters should be raised over time.
Compare it with your own fix, then rerun the tests. The editor is not changed when you open this explanation.
The reference patch unlocks after you run code you have edited.
Inspect the actual test harness
The browser and the lab download run the same cases, including cases that catch common wrong fixes, such as comparing with one fixture value instead of the real rule. The download includes the complete test adapter and cryptographic fixtures.
Download the grader, python/cases.mjsHow the browser runs your Python
Your code runs in CPython 3.14.2 compiled to WebAssembly (Pyodide 314.0.7), in a Web Worker of its own. The test cases and the pass or fail verdict run in a second worker, which only sends your functions their inputs and reads back the values they return, so changing built-ins or the harness inside Python cannot change the result. All the cases together have a 8-second limit.
The first run downloads about 6.5 MB from this site, which your browser then keeps; later runs start in a second or two. Other pages never load it.
Available: the Python standard library, including sqlite3, ipaddress, pathlib, os.path and urllib.parse, and print(), whose output appears with your results. This lab also loads pycparser 3.0, cffi 2.0.0, argon2-cffi-bindings 25.1.0 and argon2-cffi 23.1.0: the exact wheels pinned by Pyodide 314.0.7, served from this site and checked against the lock file’s SHA-256 digests.
Not available: network access, subprocesses and threads, and other packages from PyPI. The lab download runs the same cases with your local python3 (3.12 or newer), once argon2-cffi==23.1.0 is installed (its README shows how).
This lab models the verify-and-upgrade step; registration, lockout, rate limiting and breach checks are separate. It uses Argon2id by choice; scrypt, bcrypt (work factor ≥ 10, 72-byte input limit) and PBKDF2 (SHA-256 ≥ 600,000) are acceptable alternatives at the OWASP minimums. Rehash-on-login only upgrades accounts that log in; plan how to migrate or expire the rest. check_needs_rehash reports any parameter change, so a hasher weaker than a stored hash would downgrade it — keep the policy at or above the stored strength.
Your next task: In your own login path, do you rehash a password when the stored parameters fall below current policy, and do your parameters meet the current OWASP minimum?
OWASP Password Storage Cheat Sheet: Argon2id and upgrading hashes
Related guide: Password storage: hashing parameters and rehash on login