📝 Update tutorial/security/oauth2-jwt/ to use pwdlib with Argon2 instead of passlib
Project: tiangolo/fastapi | Language: Python | Concepts: security, dependency-management, backwards-compatibility | Difficulty: mid
Link: https://github.com/fastapi/fastapi/pull/13917
The problem
The FastAPI security tutorial and example code used the passlib library with the bcrypt algorithm for password hashing. passlib is no longer actively maintained and relies on Python's crypt module, which is deprecated and will be removed in Python 3.13. This creates compatibility and security concerns for current and future FastAPI users.
Why it's non-trivial
A naive fix would be to simply swap out passlib for another library in the code, but this misses several important factors:
- The documentation and code examples must be updated in sync, including all references, install instructions, and sample hashes.
- The new library (
pwdlib) and algorithm (Argon2) have different APIs and output formats, requiring careful changes to code and sample data. - Backwards compatibility and user education about the change are important, especially since many users may still have bcrypt hashes or expect legacy algorithm support.
The approach
The PR updates both documentation and code examples to use pwdlib with Argon2, replacing all uses of passlib and bcrypt.
1. Update documentation references and install instructions:
-* Implement and use well-known cryptographic tools, like Passlib and JWT tokens, etc.
+* Implement and use well-known cryptographic tools, like pwdlib and JWT tokens, etc.
This updates the general security advice to recommend pwdlib instead of passlib.
-## Install `passlib` { #install-passlib }
+## Install `pwdlib` { #install-pwdlib }
...
-$ pip install "passlib[bcrypt]"
+$ pip install "pwdlib[argon2]"
This changes the installation instructions and section headers to use pwdlib and Argon2.
2. Update code examples to use pwdlib and Argon2:
-from passlib.context import CryptContext
+from pwdlib import PasswordHash
...
-pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
+password_hash = PasswordHash.recommended()
Instead of creating a CryptContext from passlib, the code now uses PasswordHash.recommended() from pwdlib, which is configured for Argon2.
3. Update password hashing and verification functions:
-def verify_password(plain_password, hashed_password):
- return pwd_context.verify(plain_password, hashed_password)
+def verify_password(plain_password, hashed_password):
+ return password_hash.verify(plain_password, hashed_password)
...
-def get_password_hash(password):
- return pwd_context.hash(password)
+def get_password_hash(password):
+ return password_hash.hash(password)
All password hashing and verification now use the new password_hash object.
4. Update example user data to use Argon2 hashes:
- "hashed_password": "$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW",
+ "hashed_password": "$argon2id$v=19$m=65536,t=3,p=4$wagCPXjifgvUFBzq4hqe3w$CYaIb8sB+wtD+Vu/P4uod1+Qof8h+1g7bbDlBID48Rc",
All sample user hashes are replaced with Argon2id hashes generated by pwdlib.
5. Update all relevant code files and documentation mentions:
Every code sample and documentation page referencing passlib or bcrypt is updated to pwdlib and Argon2, including tips and notes for users about legacy support and migration.
From the review thread
YuriiMotov:
A few notes:
- This makes translations of
tutorial/security/oauth2-jwt/not valid (code example is updated, but descriptions are not). Should we invalidate those translations?- We should probably remove passlib from
project.tomlandrequirements-tests.txt- Replace passlib in
conditional-openapi.md
Neizvestnyj:
Thank you for your response and your feedback. I’ve taken it into account and removedpasslibfrompyproject.toml, replaced it withpwdlibinrequirements-tests.txt, updated the reference topasslibindocs/en/docs/how-to/conditional-openapi.md, and updated all relevant examples that used this library.
As for the tutorials in other languages, I think we have two options: either mark them as invalid, as you suggested, or try translating them ourselves using some translation tools and then wait for native speakers to correct them if the translation isn't good.
Takeaways
- When a core dependency becomes deprecated or unmaintained, update both code and documentation in lockstep, including all references, install instructions, and sample data.
- When switching security libraries or algorithms, ensure all example data (such as password hashes) is regenerated to match the new format and algorithm.
- Consider the impact of documentation changes on translations and internationalization; major updates may require invalidating or re-translating non-English docs.