]> git.ipfire.org Git - thirdparty/fastapi/fastapi.git/commitdiff
📝 Update `tutorial/security/oauth2-jwt/` to use `pwdlib` with Argon2 instead of ...
authorNeizvestnyj <pikromat1995@gmail.com>
Mon, 29 Sep 2025 02:57:38 +0000 (05:57 +0300)
committerGitHub <noreply@github.com>
Mon, 29 Sep 2025 02:57:38 +0000 (04:57 +0200)
Co-authored-by: Motov Yurii <109919500+YuriiMotov@users.noreply.github.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
15 files changed:
docs/en/docs/how-to/conditional-openapi.md
docs/en/docs/tutorial/security/oauth2-jwt.md
docs_src/security/tutorial004.py
docs_src/security/tutorial004_an.py
docs_src/security/tutorial004_an_py310.py
docs_src/security/tutorial004_an_py39.py
docs_src/security/tutorial004_py310.py
docs_src/security/tutorial005.py
docs_src/security/tutorial005_an.py
docs_src/security/tutorial005_an_py310.py
docs_src/security/tutorial005_an_py39.py
docs_src/security/tutorial005_py310.py
docs_src/security/tutorial005_py39.py
pyproject.toml
requirements-tests.txt

index 833123e6a7cba35f3b403a642ff33270699e6b40..e5893e584bdad161441dd6d280fecc384f98a794 100644 (file)
@@ -17,7 +17,7 @@ If you want to secure your API, there are several better things you can do, for
 * Make sure you have well defined Pydantic models for your request bodies and responses.
 * Configure any required permissions and roles using dependencies.
 * Never store plaintext passwords, only password hashes.
-* 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.
 * Add more granular permission controls with OAuth2 scopes where needed.
 * ...etc.
 
index e9ae0db680100a1949493145d53a03cdbf5dfb4b..95baf871c1e67e79e1d6e31136ab526b2404b1af 100644 (file)
@@ -64,20 +64,20 @@ If your database is stolen, the thief won't have your users' plaintext passwords
 
 So, the thief won't be able to try to use that password in another system (as many users use the same password everywhere, this would be dangerous).
 
-## Install `passlib` { #install-passlib }
+## Install `pwdlib` { #install-pwdlib }
 
-PassLib is a great Python package to handle password hashes.
+pwdlib is a great Python package to handle password hashes.
 
 It supports many secure hashing algorithms and utilities to work with them.
 
-The recommended algorithm is "Bcrypt".
+The recommended algorithm is "Argon2".
 
-Make sure you create a [virtual environment](../../virtual-environments.md){.internal-link target=_blank}, activate it, and then install PassLib with Bcrypt:
+Make sure you create a [virtual environment](../../virtual-environments.md){.internal-link target=_blank}, activate it, and then install pwdlib with Argon2:
 
 <div class="termy">
 
 ```console
-$ pip install "passlib[bcrypt]"
+$ pip install "pwdlib[argon2]"
 
 ---> 100%
 ```
@@ -86,7 +86,7 @@ $ pip install "passlib[bcrypt]"
 
 /// tip
 
-With `passlib`, you could even configure it to be able to read passwords created by **Django**, a **Flask** security plug-in or many others.
+With `pwdlib`, you could even configure it to be able to read passwords created by **Django**, a **Flask** security plug-in or many others.
 
 So, you would be able to, for example, share the same data from a Django application in a database with a FastAPI application. Or gradually migrate a Django application using the same database.
 
@@ -96,15 +96,15 @@ And your users would be able to login from your Django app or from your **FastAP
 
 ## Hash and verify the passwords { #hash-and-verify-the-passwords }
 
-Import the tools we need from `passlib`.
+Import the tools we need from `pwdlib`.
 
-Create a PassLib "context". This is what will be used to hash and verify passwords.
+Create a PasswordHash instance with recommended settings - it will be used for hashing and verifying passwords.
 
 /// tip
 
-The PassLib context also has functionality to use different hashing algorithms, including deprecated old ones only to allow verifying them, etc.
+pwdlib also supports the bcrypt hashing algorithm but does not include legacy algorithms - for working with outdated hashes, it is recommended to use the passlib library.
 
-For example, you could use it to read and verify passwords generated by another system (like Django) but hash any new passwords with a different algorithm like Bcrypt.
+For example, you could use it to read and verify passwords generated by another system (like Django) but hash any new passwords with a different algorithm like Argon2 or Bcrypt.
 
 And be compatible with all of them at the same time.
 
@@ -120,7 +120,7 @@ And another one to authenticate and return a user.
 
 /// note
 
-If you check the new (fake) database `fake_users_db`, you will see how the hashed password looks like now: `"$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW"`.
+If you check the new (fake) database `fake_users_db`, you will see how the hashed password looks like now: `"$argon2id$v=19$m=65536,t=3,p=4$wagCPXjifgvUFBzq4hqe3w$CYaIb8sB+wtD+Vu/P4uod1+Qof8h+1g7bbDlBID48Rc"`.
 
 ///
 
@@ -264,7 +264,7 @@ Many packages that simplify it a lot have to make many compromises with the data
 
 It gives you all the flexibility to choose the ones that fit your project the best.
 
-And you can use directly many well maintained and widely used packages like `passlib` and `PyJWT`, because **FastAPI** doesn't require any complex mechanisms to integrate external packages.
+And you can use directly many well maintained and widely used packages like `pwdlib` and `PyJWT`, because **FastAPI** doesn't require any complex mechanisms to integrate external packages.
 
 But it provides you the tools to simplify the process as much as possible without compromising flexibility, robustness, or security.
 
index 2225896186b8908f6f1c487791150bbe29aca303..130dc699a086dde4ff05ba8c2b6f046fa4c9e3ce 100644 (file)
@@ -5,7 +5,7 @@ import jwt
 from fastapi import Depends, FastAPI, HTTPException, status
 from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
 from jwt.exceptions import InvalidTokenError
-from passlib.context import CryptContext
+from pwdlib import PasswordHash
 from pydantic import BaseModel
 
 # to get a string like this run:
@@ -20,7 +20,7 @@ fake_users_db = {
         "username": "johndoe",
         "full_name": "John Doe",
         "email": "johndoe@example.com",
-        "hashed_password": "$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW",
+        "hashed_password": "$argon2id$v=19$m=65536,t=3,p=4$wagCPXjifgvUFBzq4hqe3w$CYaIb8sB+wtD+Vu/P4uod1+Qof8h+1g7bbDlBID48Rc",
         "disabled": False,
     }
 }
@@ -46,7 +46,7 @@ class UserInDB(User):
     hashed_password: str
 
 
-pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
+password_hash = PasswordHash.recommended()
 
 oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
 
@@ -54,11 +54,11 @@ app = FastAPI()
 
 
 def verify_password(plain_password, hashed_password):
-    return pwd_context.verify(plain_password, hashed_password)
+    return password_hash.verify(plain_password, hashed_password)
 
 
 def get_password_hash(password):
-    return pwd_context.hash(password)
+    return password_hash.hash(password)
 
 
 def get_user(db, username: str):
index e2221cd3996475492cd9953f4898aa6076662cd0..018234e300f009160ccbb58fcd11afa2f1f030a8 100644 (file)
@@ -5,7 +5,7 @@ import jwt
 from fastapi import Depends, FastAPI, HTTPException, status
 from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
 from jwt.exceptions import InvalidTokenError
-from passlib.context import CryptContext
+from pwdlib import PasswordHash
 from pydantic import BaseModel
 from typing_extensions import Annotated
 
@@ -21,7 +21,7 @@ fake_users_db = {
         "username": "johndoe",
         "full_name": "John Doe",
         "email": "johndoe@example.com",
-        "hashed_password": "$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW",
+        "hashed_password": "$argon2id$v=19$m=65536,t=3,p=4$wagCPXjifgvUFBzq4hqe3w$CYaIb8sB+wtD+Vu/P4uod1+Qof8h+1g7bbDlBID48Rc",
         "disabled": False,
     }
 }
@@ -47,7 +47,7 @@ class UserInDB(User):
     hashed_password: str
 
 
-pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
+password_hash = PasswordHash.recommended()
 
 oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
 
@@ -55,11 +55,11 @@ app = FastAPI()
 
 
 def verify_password(plain_password, hashed_password):
-    return pwd_context.verify(plain_password, hashed_password)
+    return password_hash.verify(plain_password, hashed_password)
 
 
 def get_password_hash(password):
-    return pwd_context.hash(password)
+    return password_hash.hash(password)
 
 
 def get_user(db, username: str):
index a3f74fc0e54183e4d6183ec9b09650d32e341ded..18ea96bc5cdb42ac387cf8723aef1eb818b9ecee 100644 (file)
@@ -5,7 +5,7 @@ import jwt
 from fastapi import Depends, FastAPI, HTTPException, status
 from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
 from jwt.exceptions import InvalidTokenError
-from passlib.context import CryptContext
+from pwdlib import PasswordHash
 from pydantic import BaseModel
 
 # to get a string like this run:
@@ -20,7 +20,7 @@ fake_users_db = {
         "username": "johndoe",
         "full_name": "John Doe",
         "email": "johndoe@example.com",
-        "hashed_password": "$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW",
+        "hashed_password": "$argon2id$v=19$m=65536,t=3,p=4$wagCPXjifgvUFBzq4hqe3w$CYaIb8sB+wtD+Vu/P4uod1+Qof8h+1g7bbDlBID48Rc",
         "disabled": False,
     }
 }
@@ -46,7 +46,7 @@ class UserInDB(User):
     hashed_password: str
 
 
-pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
+password_hash = PasswordHash.recommended()
 
 oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
 
@@ -54,11 +54,11 @@ app = FastAPI()
 
 
 def verify_password(plain_password, hashed_password):
-    return pwd_context.verify(plain_password, hashed_password)
+    return password_hash.verify(plain_password, hashed_password)
 
 
 def get_password_hash(password):
-    return pwd_context.hash(password)
+    return password_hash.hash(password)
 
 
 def get_user(db, username: str):
index b33d677ed196887a88c5194442ac64fdfdca58bd..d3fd29e5a5c847ffcbcfc982d1b8fb1a7bbb8aa2 100644 (file)
@@ -5,7 +5,7 @@ import jwt
 from fastapi import Depends, FastAPI, HTTPException, status
 from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
 from jwt.exceptions import InvalidTokenError
-from passlib.context import CryptContext
+from pwdlib import PasswordHash
 from pydantic import BaseModel
 
 # to get a string like this run:
@@ -20,7 +20,7 @@ fake_users_db = {
         "username": "johndoe",
         "full_name": "John Doe",
         "email": "johndoe@example.com",
-        "hashed_password": "$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW",
+        "hashed_password": "$argon2id$v=19$m=65536,t=3,p=4$wagCPXjifgvUFBzq4hqe3w$CYaIb8sB+wtD+Vu/P4uod1+Qof8h+1g7bbDlBID48Rc",
         "disabled": False,
     }
 }
@@ -46,7 +46,7 @@ class UserInDB(User):
     hashed_password: str
 
 
-pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
+password_hash = PasswordHash.recommended()
 
 oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
 
@@ -54,11 +54,11 @@ app = FastAPI()
 
 
 def verify_password(plain_password, hashed_password):
-    return pwd_context.verify(plain_password, hashed_password)
+    return password_hash.verify(plain_password, hashed_password)
 
 
 def get_password_hash(password):
-    return pwd_context.hash(password)
+    return password_hash.hash(password)
 
 
 def get_user(db, username: str):
index d46ce26bf8dbd673f553f2f8510f0d273b0f88a0..cd1dcff4604032233597610cab42e804884ace4c 100644 (file)
@@ -4,7 +4,7 @@ import jwt
 from fastapi import Depends, FastAPI, HTTPException, status
 from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
 from jwt.exceptions import InvalidTokenError
-from passlib.context import CryptContext
+from pwdlib import PasswordHash
 from pydantic import BaseModel
 
 # to get a string like this run:
@@ -19,7 +19,7 @@ fake_users_db = {
         "username": "johndoe",
         "full_name": "John Doe",
         "email": "johndoe@example.com",
-        "hashed_password": "$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW",
+        "hashed_password": "$argon2id$v=19$m=65536,t=3,p=4$wagCPXjifgvUFBzq4hqe3w$CYaIb8sB+wtD+Vu/P4uod1+Qof8h+1g7bbDlBID48Rc",
         "disabled": False,
     }
 }
@@ -45,7 +45,7 @@ class UserInDB(User):
     hashed_password: str
 
 
-pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
+password_hash = PasswordHash.recommended()
 
 oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
 
@@ -53,11 +53,11 @@ app = FastAPI()
 
 
 def verify_password(plain_password, hashed_password):
-    return pwd_context.verify(plain_password, hashed_password)
+    return password_hash.verify(plain_password, hashed_password)
 
 
 def get_password_hash(password):
-    return pwd_context.hash(password)
+    return password_hash.hash(password)
 
 
 def get_user(db, username: str):
index 447dacb3724df32980df3a693dc7a23d7d6e0896..fdd73bcd8a43855759360a8dfa047fc4871f5436 100644 (file)
@@ -9,7 +9,7 @@ from fastapi.security import (
     SecurityScopes,
 )
 from jwt.exceptions import InvalidTokenError
-from passlib.context import CryptContext
+from pwdlib import PasswordHash
 from pydantic import BaseModel, ValidationError
 
 # to get a string like this run:
@@ -24,14 +24,14 @@ fake_users_db = {
         "username": "johndoe",
         "full_name": "John Doe",
         "email": "johndoe@example.com",
-        "hashed_password": "$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW",
+        "hashed_password": "$argon2id$v=19$m=65536,t=3,p=4$wagCPXjifgvUFBzq4hqe3w$CYaIb8sB+wtD+Vu/P4uod1+Qof8h+1g7bbDlBID48Rc",
         "disabled": False,
     },
     "alice": {
         "username": "alice",
         "full_name": "Alice Chains",
         "email": "alicechains@example.com",
-        "hashed_password": "$2b$12$gSvqqUPvlXP2tfVFaWK1Be7DlH.PKZbv5H8KnzzVgXXbVxpva.pFm",
+        "hashed_password": "$argon2id$v=19$m=65536,t=3,p=4$g2/AV1zwopqUntPKJavBFw$BwpRGDCyUHLvHICnwijyX8ROGoiUPwNKZ7915MeYfCE",
         "disabled": True,
     },
 }
@@ -58,7 +58,7 @@ class UserInDB(User):
     hashed_password: str
 
 
-pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
+password_hash = PasswordHash.recommended()
 
 oauth2_scheme = OAuth2PasswordBearer(
     tokenUrl="token",
@@ -69,11 +69,11 @@ app = FastAPI()
 
 
 def verify_password(plain_password, hashed_password):
-    return pwd_context.verify(plain_password, hashed_password)
+    return password_hash.verify(plain_password, hashed_password)
 
 
 def get_password_hash(password):
-    return pwd_context.hash(password)
+    return password_hash.hash(password)
 
 
 def get_user(db, username: str):
index d2c4fe9b8d2fabcbd9153a4ab3ae62e1a1ae989e..e1d7b4f62a2436a1f6f60e330a4e9e86e699bdeb 100644 (file)
@@ -9,7 +9,7 @@ from fastapi.security import (
     SecurityScopes,
 )
 from jwt.exceptions import InvalidTokenError
-from passlib.context import CryptContext
+from pwdlib import PasswordHash
 from pydantic import BaseModel, ValidationError
 from typing_extensions import Annotated
 
@@ -25,14 +25,14 @@ fake_users_db = {
         "username": "johndoe",
         "full_name": "John Doe",
         "email": "johndoe@example.com",
-        "hashed_password": "$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW",
+        "hashed_password": "$argon2id$v=19$m=65536,t=3,p=4$wagCPXjifgvUFBzq4hqe3w$CYaIb8sB+wtD+Vu/P4uod1+Qof8h+1g7bbDlBID48Rc",
         "disabled": False,
     },
     "alice": {
         "username": "alice",
         "full_name": "Alice Chains",
         "email": "alicechains@example.com",
-        "hashed_password": "$2b$12$gSvqqUPvlXP2tfVFaWK1Be7DlH.PKZbv5H8KnzzVgXXbVxpva.pFm",
+        "hashed_password": "$argon2id$v=19$m=65536,t=3,p=4$g2/AV1zwopqUntPKJavBFw$BwpRGDCyUHLvHICnwijyX8ROGoiUPwNKZ7915MeYfCE",
         "disabled": True,
     },
 }
@@ -59,7 +59,7 @@ class UserInDB(User):
     hashed_password: str
 
 
-pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
+password_hash = PasswordHash.recommended()
 
 oauth2_scheme = OAuth2PasswordBearer(
     tokenUrl="token",
@@ -70,11 +70,11 @@ app = FastAPI()
 
 
 def verify_password(plain_password, hashed_password):
-    return pwd_context.verify(plain_password, hashed_password)
+    return password_hash.verify(plain_password, hashed_password)
 
 
 def get_password_hash(password):
-    return pwd_context.hash(password)
+    return password_hash.hash(password)
 
 
 def get_user(db, username: str):
index e3527370d893a4267a60ada266d4b6ad38b758ce..df55951c074c938f5cbaaec1df35851af6dc6bef 100644 (file)
@@ -9,7 +9,7 @@ from fastapi.security import (
     SecurityScopes,
 )
 from jwt.exceptions import InvalidTokenError
-from passlib.context import CryptContext
+from pwdlib import PasswordHash
 from pydantic import BaseModel, ValidationError
 
 # to get a string like this run:
@@ -24,14 +24,14 @@ fake_users_db = {
         "username": "johndoe",
         "full_name": "John Doe",
         "email": "johndoe@example.com",
-        "hashed_password": "$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW",
+        "hashed_password": "$argon2id$v=19$m=65536,t=3,p=4$wagCPXjifgvUFBzq4hqe3w$CYaIb8sB+wtD+Vu/P4uod1+Qof8h+1g7bbDlBID48Rc",
         "disabled": False,
     },
     "alice": {
         "username": "alice",
         "full_name": "Alice Chains",
         "email": "alicechains@example.com",
-        "hashed_password": "$2b$12$gSvqqUPvlXP2tfVFaWK1Be7DlH.PKZbv5H8KnzzVgXXbVxpva.pFm",
+        "hashed_password": "$argon2id$v=19$m=65536,t=3,p=4$g2/AV1zwopqUntPKJavBFw$BwpRGDCyUHLvHICnwijyX8ROGoiUPwNKZ7915MeYfCE",
         "disabled": True,
     },
 }
@@ -58,7 +58,7 @@ class UserInDB(User):
     hashed_password: str
 
 
-pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
+password_hash = PasswordHash.recommended()
 
 oauth2_scheme = OAuth2PasswordBearer(
     tokenUrl="token",
@@ -69,11 +69,11 @@ app = FastAPI()
 
 
 def verify_password(plain_password, hashed_password):
-    return pwd_context.verify(plain_password, hashed_password)
+    return password_hash.verify(plain_password, hashed_password)
 
 
 def get_password_hash(password):
-    return pwd_context.hash(password)
+    return password_hash.hash(password)
 
 
 def get_user(db, username: str):
index 3dc3140c3be0d02ed2d713b772d8be2ec73af484..983c1c22cf93072a607a0072de74e68d3618abfd 100644 (file)
@@ -9,7 +9,7 @@ from fastapi.security import (
     SecurityScopes,
 )
 from jwt.exceptions import InvalidTokenError
-from passlib.context import CryptContext
+from pwdlib import PasswordHash
 from pydantic import BaseModel, ValidationError
 
 # to get a string like this run:
@@ -24,14 +24,14 @@ fake_users_db = {
         "username": "johndoe",
         "full_name": "John Doe",
         "email": "johndoe@example.com",
-        "hashed_password": "$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW",
+        "hashed_password": "$argon2id$v=19$m=65536,t=3,p=4$wagCPXjifgvUFBzq4hqe3w$CYaIb8sB+wtD+Vu/P4uod1+Qof8h+1g7bbDlBID48Rc",
         "disabled": False,
     },
     "alice": {
         "username": "alice",
         "full_name": "Alice Chains",
         "email": "alicechains@example.com",
-        "hashed_password": "$2b$12$gSvqqUPvlXP2tfVFaWK1Be7DlH.PKZbv5H8KnzzVgXXbVxpva.pFm",
+        "hashed_password": "$argon2id$v=19$m=65536,t=3,p=4$g2/AV1zwopqUntPKJavBFw$BwpRGDCyUHLvHICnwijyX8ROGoiUPwNKZ7915MeYfCE",
         "disabled": True,
     },
 }
@@ -58,7 +58,7 @@ class UserInDB(User):
     hashed_password: str
 
 
-pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
+password_hash = PasswordHash.recommended()
 
 oauth2_scheme = OAuth2PasswordBearer(
     tokenUrl="token",
@@ -69,11 +69,11 @@ app = FastAPI()
 
 
 def verify_password(plain_password, hashed_password):
-    return pwd_context.verify(plain_password, hashed_password)
+    return password_hash.verify(plain_password, hashed_password)
 
 
 def get_password_hash(password):
-    return pwd_context.hash(password)
+    return password_hash.hash(password)
 
 
 def get_user(db, username: str):
index 3fc15212bd29a6a37f65b1b2945bce7e4e345cb6..d08e2c59f33b0ea2e727ff5d69bae45c7195517f 100644 (file)
@@ -8,7 +8,7 @@ from fastapi.security import (
     SecurityScopes,
 )
 from jwt.exceptions import InvalidTokenError
-from passlib.context import CryptContext
+from pwdlib import PasswordHash
 from pydantic import BaseModel, ValidationError
 
 # to get a string like this run:
@@ -23,14 +23,14 @@ fake_users_db = {
         "username": "johndoe",
         "full_name": "John Doe",
         "email": "johndoe@example.com",
-        "hashed_password": "$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW",
+        "hashed_password": "$argon2id$v=19$m=65536,t=3,p=4$wagCPXjifgvUFBzq4hqe3w$CYaIb8sB+wtD+Vu/P4uod1+Qof8h+1g7bbDlBID48Rc",
         "disabled": False,
     },
     "alice": {
         "username": "alice",
         "full_name": "Alice Chains",
         "email": "alicechains@example.com",
-        "hashed_password": "$2b$12$gSvqqUPvlXP2tfVFaWK1Be7DlH.PKZbv5H8KnzzVgXXbVxpva.pFm",
+        "hashed_password": "$argon2id$v=19$m=65536,t=3,p=4$g2/AV1zwopqUntPKJavBFw$BwpRGDCyUHLvHICnwijyX8ROGoiUPwNKZ7915MeYfCE",
         "disabled": True,
     },
 }
@@ -57,7 +57,7 @@ class UserInDB(User):
     hashed_password: str
 
 
-pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
+password_hash = PasswordHash.recommended()
 
 oauth2_scheme = OAuth2PasswordBearer(
     tokenUrl="token",
@@ -68,11 +68,11 @@ app = FastAPI()
 
 
 def verify_password(plain_password, hashed_password):
-    return pwd_context.verify(plain_password, hashed_password)
+    return password_hash.verify(plain_password, hashed_password)
 
 
 def get_password_hash(password):
-    return pwd_context.hash(password)
+    return password_hash.hash(password)
 
 
 def get_user(db, username: str):
index f9aed0a42a5f1ee80e76b5caaf2c7dabecd3aace..5bde47ef478c04fca709302b37004f591d6c9be3 100644 (file)
@@ -9,7 +9,7 @@ from fastapi.security import (
     SecurityScopes,
 )
 from jwt.exceptions import InvalidTokenError
-from passlib.context import CryptContext
+from pwdlib import PasswordHash
 from pydantic import BaseModel, ValidationError
 
 # to get a string like this run:
@@ -24,14 +24,14 @@ fake_users_db = {
         "username": "johndoe",
         "full_name": "John Doe",
         "email": "johndoe@example.com",
-        "hashed_password": "$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW",
+        "hashed_password": "$argon2id$v=19$m=65536,t=3,p=4$wagCPXjifgvUFBzq4hqe3w$CYaIb8sB+wtD+Vu/P4uod1+Qof8h+1g7bbDlBID48Rc",
         "disabled": False,
     },
     "alice": {
         "username": "alice",
         "full_name": "Alice Chains",
         "email": "alicechains@example.com",
-        "hashed_password": "$2b$12$gSvqqUPvlXP2tfVFaWK1Be7DlH.PKZbv5H8KnzzVgXXbVxpva.pFm",
+        "hashed_password": "$argon2id$v=19$m=65536,t=3,p=4$g2/AV1zwopqUntPKJavBFw$BwpRGDCyUHLvHICnwijyX8ROGoiUPwNKZ7915MeYfCE",
         "disabled": True,
     },
 }
@@ -58,7 +58,7 @@ class UserInDB(User):
     hashed_password: str
 
 
-pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
+password_hash = PasswordHash.recommended()
 
 oauth2_scheme = OAuth2PasswordBearer(
     tokenUrl="token",
@@ -69,11 +69,11 @@ app = FastAPI()
 
 
 def verify_password(plain_password, hashed_password):
-    return pwd_context.verify(plain_password, hashed_password)
+    return password_hash.verify(plain_password, hashed_password)
 
 
 def get_password_hash(password):
-    return pwd_context.hash(password)
+    return password_hash.hash(password)
 
 
 def get_user(db, username: str):
index fbfdea79a45045d9d12eb012eadcdd43567197d3..41ef1eb768ed9a9e71956881cbb5cd105f47e81b 100644 (file)
@@ -172,8 +172,6 @@ junit_family = "xunit2"
 filterwarnings = [
     "error",
     'ignore:starlette.middleware.wsgi is deprecated and will be removed in a future release\..*:DeprecationWarning:starlette',
-    # For passlib
-    "ignore:'crypt' is deprecated and slated for removal in Python 3.13:DeprecationWarning",
     # see https://trio.readthedocs.io/en/stable/history.html#trio-0-22-0-2022-09-28
     "ignore:You seem to already have a custom.*:RuntimeWarning:trio",
     # TODO: remove after upgrading SQLAlchemy to a version that includes the following changes
index e87a42162cd28957ee53183b492679608bebcac8..53ec28d2eaefdb12ca5c46c8ccb9d1f495b10e4b 100644 (file)
@@ -9,7 +9,7 @@ flask >=1.1.2,<4.0.0
 anyio[trio] >=3.2.1,<5.0.0
 PyJWT==2.9.0
 pyyaml >=5.3.1,<7.0.0
-passlib[bcrypt] >=1.7.2,<2.0.0
+pwdlib[argon2] >=0.2.1
 inline-snapshot>=0.21.1
 # types
 types-ujson ==5.10.0.20240515