Browse Source

📝 Update `tutorial/security/oauth2-jwt/` to use `pwdlib` with Argon2 instead of `passlib` (#13917)

Co-authored-by: Motov Yurii <[email protected]>
Co-authored-by: Sebastián Ramírez <[email protected]>
pull/11634/merge
Neizvestnyj 5 days ago
committed by GitHub
parent
commit
efdafa4361
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 2
      docs/en/docs/how-to/conditional-openapi.md
  2. 24
      docs/en/docs/tutorial/security/oauth2-jwt.md
  3. 10
      docs_src/security/tutorial004.py
  4. 10
      docs_src/security/tutorial004_an.py
  5. 10
      docs_src/security/tutorial004_an_py310.py
  6. 10
      docs_src/security/tutorial004_an_py39.py
  7. 10
      docs_src/security/tutorial004_py310.py
  8. 12
      docs_src/security/tutorial005.py
  9. 12
      docs_src/security/tutorial005_an.py
  10. 12
      docs_src/security/tutorial005_an_py310.py
  11. 12
      docs_src/security/tutorial005_an_py39.py
  12. 12
      docs_src/security/tutorial005_py310.py
  13. 12
      docs_src/security/tutorial005_py39.py
  14. 2
      pyproject.toml
  15. 2
      requirements-tests.txt

2
docs/en/docs/how-to/conditional-openapi.md

@ -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. * Make sure you have well defined Pydantic models for your request bodies and responses.
* Configure any required permissions and roles using dependencies. * Configure any required permissions and roles using dependencies.
* Never store plaintext passwords, only password hashes. * 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. * Add more granular permission controls with OAuth2 scopes where needed.
* ...etc. * ...etc.

24
docs/en/docs/tutorial/security/oauth2-jwt.md

@ -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). 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. 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"> <div class="termy">
```console ```console
$ pip install "passlib[bcrypt]" $ pip install "pwdlib[argon2]"
---> 100% ---> 100%
``` ```
@ -86,7 +86,7 @@ $ pip install "passlib[bcrypt]"
/// tip /// 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. 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 } ## 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 /// 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. 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 /// 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. 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. But it provides you the tools to simplify the process as much as possible without compromising flexibility, robustness, or security.

10
docs_src/security/tutorial004.py

@ -5,7 +5,7 @@ import jwt
from fastapi import Depends, FastAPI, HTTPException, status from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jwt.exceptions import InvalidTokenError from jwt.exceptions import InvalidTokenError
from passlib.context import CryptContext from pwdlib import PasswordHash
from pydantic import BaseModel from pydantic import BaseModel
# to get a string like this run: # to get a string like this run:
@ -20,7 +20,7 @@ fake_users_db = {
"username": "johndoe", "username": "johndoe",
"full_name": "John Doe", "full_name": "John Doe",
"email": "[email protected]", "email": "[email protected]",
"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, "disabled": False,
} }
} }
@ -46,7 +46,7 @@ class UserInDB(User):
hashed_password: str hashed_password: str
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") password_hash = PasswordHash.recommended()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@ -54,11 +54,11 @@ app = FastAPI()
def verify_password(plain_password, hashed_password): 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): def get_password_hash(password):
return pwd_context.hash(password) return password_hash.hash(password)
def get_user(db, username: str): def get_user(db, username: str):

10
docs_src/security/tutorial004_an.py

@ -5,7 +5,7 @@ import jwt
from fastapi import Depends, FastAPI, HTTPException, status from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jwt.exceptions import InvalidTokenError from jwt.exceptions import InvalidTokenError
from passlib.context import CryptContext from pwdlib import PasswordHash
from pydantic import BaseModel from pydantic import BaseModel
from typing_extensions import Annotated from typing_extensions import Annotated
@ -21,7 +21,7 @@ fake_users_db = {
"username": "johndoe", "username": "johndoe",
"full_name": "John Doe", "full_name": "John Doe",
"email": "[email protected]", "email": "[email protected]",
"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, "disabled": False,
} }
} }
@ -47,7 +47,7 @@ class UserInDB(User):
hashed_password: str hashed_password: str
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") password_hash = PasswordHash.recommended()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@ -55,11 +55,11 @@ app = FastAPI()
def verify_password(plain_password, hashed_password): 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): def get_password_hash(password):
return pwd_context.hash(password) return password_hash.hash(password)
def get_user(db, username: str): def get_user(db, username: str):

10
docs_src/security/tutorial004_an_py310.py

@ -5,7 +5,7 @@ import jwt
from fastapi import Depends, FastAPI, HTTPException, status from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jwt.exceptions import InvalidTokenError from jwt.exceptions import InvalidTokenError
from passlib.context import CryptContext from pwdlib import PasswordHash
from pydantic import BaseModel from pydantic import BaseModel
# to get a string like this run: # to get a string like this run:
@ -20,7 +20,7 @@ fake_users_db = {
"username": "johndoe", "username": "johndoe",
"full_name": "John Doe", "full_name": "John Doe",
"email": "[email protected]", "email": "[email protected]",
"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, "disabled": False,
} }
} }
@ -46,7 +46,7 @@ class UserInDB(User):
hashed_password: str hashed_password: str
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") password_hash = PasswordHash.recommended()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@ -54,11 +54,11 @@ app = FastAPI()
def verify_password(plain_password, hashed_password): 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): def get_password_hash(password):
return pwd_context.hash(password) return password_hash.hash(password)
def get_user(db, username: str): def get_user(db, username: str):

10
docs_src/security/tutorial004_an_py39.py

@ -5,7 +5,7 @@ import jwt
from fastapi import Depends, FastAPI, HTTPException, status from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jwt.exceptions import InvalidTokenError from jwt.exceptions import InvalidTokenError
from passlib.context import CryptContext from pwdlib import PasswordHash
from pydantic import BaseModel from pydantic import BaseModel
# to get a string like this run: # to get a string like this run:
@ -20,7 +20,7 @@ fake_users_db = {
"username": "johndoe", "username": "johndoe",
"full_name": "John Doe", "full_name": "John Doe",
"email": "[email protected]", "email": "[email protected]",
"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, "disabled": False,
} }
} }
@ -46,7 +46,7 @@ class UserInDB(User):
hashed_password: str hashed_password: str
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") password_hash = PasswordHash.recommended()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@ -54,11 +54,11 @@ app = FastAPI()
def verify_password(plain_password, hashed_password): 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): def get_password_hash(password):
return pwd_context.hash(password) return password_hash.hash(password)
def get_user(db, username: str): def get_user(db, username: str):

10
docs_src/security/tutorial004_py310.py

@ -4,7 +4,7 @@ import jwt
from fastapi import Depends, FastAPI, HTTPException, status from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jwt.exceptions import InvalidTokenError from jwt.exceptions import InvalidTokenError
from passlib.context import CryptContext from pwdlib import PasswordHash
from pydantic import BaseModel from pydantic import BaseModel
# to get a string like this run: # to get a string like this run:
@ -19,7 +19,7 @@ fake_users_db = {
"username": "johndoe", "username": "johndoe",
"full_name": "John Doe", "full_name": "John Doe",
"email": "[email protected]", "email": "[email protected]",
"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, "disabled": False,
} }
} }
@ -45,7 +45,7 @@ class UserInDB(User):
hashed_password: str hashed_password: str
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") password_hash = PasswordHash.recommended()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@ -53,11 +53,11 @@ app = FastAPI()
def verify_password(plain_password, hashed_password): 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): def get_password_hash(password):
return pwd_context.hash(password) return password_hash.hash(password)
def get_user(db, username: str): def get_user(db, username: str):

12
docs_src/security/tutorial005.py

@ -9,7 +9,7 @@ from fastapi.security import (
SecurityScopes, SecurityScopes,
) )
from jwt.exceptions import InvalidTokenError from jwt.exceptions import InvalidTokenError
from passlib.context import CryptContext from pwdlib import PasswordHash
from pydantic import BaseModel, ValidationError from pydantic import BaseModel, ValidationError
# to get a string like this run: # to get a string like this run:
@ -24,14 +24,14 @@ fake_users_db = {
"username": "johndoe", "username": "johndoe",
"full_name": "John Doe", "full_name": "John Doe",
"email": "[email protected]", "email": "[email protected]",
"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, "disabled": False,
}, },
"alice": { "alice": {
"username": "alice", "username": "alice",
"full_name": "Alice Chains", "full_name": "Alice Chains",
"email": "[email protected]", "email": "[email protected]",
"hashed_password": "$2b$12$gSvqqUPvlXP2tfVFaWK1Be7DlH.PKZbv5H8KnzzVgXXbVxpva.pFm", "hashed_password": "$argon2id$v=19$m=65536,t=3,p=4$g2/AV1zwopqUntPKJavBFw$BwpRGDCyUHLvHICnwijyX8ROGoiUPwNKZ7915MeYfCE",
"disabled": True, "disabled": True,
}, },
} }
@ -58,7 +58,7 @@ class UserInDB(User):
hashed_password: str hashed_password: str
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") password_hash = PasswordHash.recommended()
oauth2_scheme = OAuth2PasswordBearer( oauth2_scheme = OAuth2PasswordBearer(
tokenUrl="token", tokenUrl="token",
@ -69,11 +69,11 @@ app = FastAPI()
def verify_password(plain_password, hashed_password): 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): def get_password_hash(password):
return pwd_context.hash(password) return password_hash.hash(password)
def get_user(db, username: str): def get_user(db, username: str):

12
docs_src/security/tutorial005_an.py

@ -9,7 +9,7 @@ from fastapi.security import (
SecurityScopes, SecurityScopes,
) )
from jwt.exceptions import InvalidTokenError from jwt.exceptions import InvalidTokenError
from passlib.context import CryptContext from pwdlib import PasswordHash
from pydantic import BaseModel, ValidationError from pydantic import BaseModel, ValidationError
from typing_extensions import Annotated from typing_extensions import Annotated
@ -25,14 +25,14 @@ fake_users_db = {
"username": "johndoe", "username": "johndoe",
"full_name": "John Doe", "full_name": "John Doe",
"email": "[email protected]", "email": "[email protected]",
"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, "disabled": False,
}, },
"alice": { "alice": {
"username": "alice", "username": "alice",
"full_name": "Alice Chains", "full_name": "Alice Chains",
"email": "[email protected]", "email": "[email protected]",
"hashed_password": "$2b$12$gSvqqUPvlXP2tfVFaWK1Be7DlH.PKZbv5H8KnzzVgXXbVxpva.pFm", "hashed_password": "$argon2id$v=19$m=65536,t=3,p=4$g2/AV1zwopqUntPKJavBFw$BwpRGDCyUHLvHICnwijyX8ROGoiUPwNKZ7915MeYfCE",
"disabled": True, "disabled": True,
}, },
} }
@ -59,7 +59,7 @@ class UserInDB(User):
hashed_password: str hashed_password: str
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") password_hash = PasswordHash.recommended()
oauth2_scheme = OAuth2PasswordBearer( oauth2_scheme = OAuth2PasswordBearer(
tokenUrl="token", tokenUrl="token",
@ -70,11 +70,11 @@ app = FastAPI()
def verify_password(plain_password, hashed_password): 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): def get_password_hash(password):
return pwd_context.hash(password) return password_hash.hash(password)
def get_user(db, username: str): def get_user(db, username: str):

12
docs_src/security/tutorial005_an_py310.py

@ -9,7 +9,7 @@ from fastapi.security import (
SecurityScopes, SecurityScopes,
) )
from jwt.exceptions import InvalidTokenError from jwt.exceptions import InvalidTokenError
from passlib.context import CryptContext from pwdlib import PasswordHash
from pydantic import BaseModel, ValidationError from pydantic import BaseModel, ValidationError
# to get a string like this run: # to get a string like this run:
@ -24,14 +24,14 @@ fake_users_db = {
"username": "johndoe", "username": "johndoe",
"full_name": "John Doe", "full_name": "John Doe",
"email": "[email protected]", "email": "[email protected]",
"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, "disabled": False,
}, },
"alice": { "alice": {
"username": "alice", "username": "alice",
"full_name": "Alice Chains", "full_name": "Alice Chains",
"email": "[email protected]", "email": "[email protected]",
"hashed_password": "$2b$12$gSvqqUPvlXP2tfVFaWK1Be7DlH.PKZbv5H8KnzzVgXXbVxpva.pFm", "hashed_password": "$argon2id$v=19$m=65536,t=3,p=4$g2/AV1zwopqUntPKJavBFw$BwpRGDCyUHLvHICnwijyX8ROGoiUPwNKZ7915MeYfCE",
"disabled": True, "disabled": True,
}, },
} }
@ -58,7 +58,7 @@ class UserInDB(User):
hashed_password: str hashed_password: str
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") password_hash = PasswordHash.recommended()
oauth2_scheme = OAuth2PasswordBearer( oauth2_scheme = OAuth2PasswordBearer(
tokenUrl="token", tokenUrl="token",
@ -69,11 +69,11 @@ app = FastAPI()
def verify_password(plain_password, hashed_password): 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): def get_password_hash(password):
return pwd_context.hash(password) return password_hash.hash(password)
def get_user(db, username: str): def get_user(db, username: str):

12
docs_src/security/tutorial005_an_py39.py

@ -9,7 +9,7 @@ from fastapi.security import (
SecurityScopes, SecurityScopes,
) )
from jwt.exceptions import InvalidTokenError from jwt.exceptions import InvalidTokenError
from passlib.context import CryptContext from pwdlib import PasswordHash
from pydantic import BaseModel, ValidationError from pydantic import BaseModel, ValidationError
# to get a string like this run: # to get a string like this run:
@ -24,14 +24,14 @@ fake_users_db = {
"username": "johndoe", "username": "johndoe",
"full_name": "John Doe", "full_name": "John Doe",
"email": "[email protected]", "email": "[email protected]",
"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, "disabled": False,
}, },
"alice": { "alice": {
"username": "alice", "username": "alice",
"full_name": "Alice Chains", "full_name": "Alice Chains",
"email": "[email protected]", "email": "[email protected]",
"hashed_password": "$2b$12$gSvqqUPvlXP2tfVFaWK1Be7DlH.PKZbv5H8KnzzVgXXbVxpva.pFm", "hashed_password": "$argon2id$v=19$m=65536,t=3,p=4$g2/AV1zwopqUntPKJavBFw$BwpRGDCyUHLvHICnwijyX8ROGoiUPwNKZ7915MeYfCE",
"disabled": True, "disabled": True,
}, },
} }
@ -58,7 +58,7 @@ class UserInDB(User):
hashed_password: str hashed_password: str
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") password_hash = PasswordHash.recommended()
oauth2_scheme = OAuth2PasswordBearer( oauth2_scheme = OAuth2PasswordBearer(
tokenUrl="token", tokenUrl="token",
@ -69,11 +69,11 @@ app = FastAPI()
def verify_password(plain_password, hashed_password): 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): def get_password_hash(password):
return pwd_context.hash(password) return password_hash.hash(password)
def get_user(db, username: str): def get_user(db, username: str):

12
docs_src/security/tutorial005_py310.py

@ -8,7 +8,7 @@ from fastapi.security import (
SecurityScopes, SecurityScopes,
) )
from jwt.exceptions import InvalidTokenError from jwt.exceptions import InvalidTokenError
from passlib.context import CryptContext from pwdlib import PasswordHash
from pydantic import BaseModel, ValidationError from pydantic import BaseModel, ValidationError
# to get a string like this run: # to get a string like this run:
@ -23,14 +23,14 @@ fake_users_db = {
"username": "johndoe", "username": "johndoe",
"full_name": "John Doe", "full_name": "John Doe",
"email": "[email protected]", "email": "[email protected]",
"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, "disabled": False,
}, },
"alice": { "alice": {
"username": "alice", "username": "alice",
"full_name": "Alice Chains", "full_name": "Alice Chains",
"email": "[email protected]", "email": "[email protected]",
"hashed_password": "$2b$12$gSvqqUPvlXP2tfVFaWK1Be7DlH.PKZbv5H8KnzzVgXXbVxpva.pFm", "hashed_password": "$argon2id$v=19$m=65536,t=3,p=4$g2/AV1zwopqUntPKJavBFw$BwpRGDCyUHLvHICnwijyX8ROGoiUPwNKZ7915MeYfCE",
"disabled": True, "disabled": True,
}, },
} }
@ -57,7 +57,7 @@ class UserInDB(User):
hashed_password: str hashed_password: str
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") password_hash = PasswordHash.recommended()
oauth2_scheme = OAuth2PasswordBearer( oauth2_scheme = OAuth2PasswordBearer(
tokenUrl="token", tokenUrl="token",
@ -68,11 +68,11 @@ app = FastAPI()
def verify_password(plain_password, hashed_password): 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): def get_password_hash(password):
return pwd_context.hash(password) return password_hash.hash(password)
def get_user(db, username: str): def get_user(db, username: str):

12
docs_src/security/tutorial005_py39.py

@ -9,7 +9,7 @@ from fastapi.security import (
SecurityScopes, SecurityScopes,
) )
from jwt.exceptions import InvalidTokenError from jwt.exceptions import InvalidTokenError
from passlib.context import CryptContext from pwdlib import PasswordHash
from pydantic import BaseModel, ValidationError from pydantic import BaseModel, ValidationError
# to get a string like this run: # to get a string like this run:
@ -24,14 +24,14 @@ fake_users_db = {
"username": "johndoe", "username": "johndoe",
"full_name": "John Doe", "full_name": "John Doe",
"email": "[email protected]", "email": "[email protected]",
"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, "disabled": False,
}, },
"alice": { "alice": {
"username": "alice", "username": "alice",
"full_name": "Alice Chains", "full_name": "Alice Chains",
"email": "[email protected]", "email": "[email protected]",
"hashed_password": "$2b$12$gSvqqUPvlXP2tfVFaWK1Be7DlH.PKZbv5H8KnzzVgXXbVxpva.pFm", "hashed_password": "$argon2id$v=19$m=65536,t=3,p=4$g2/AV1zwopqUntPKJavBFw$BwpRGDCyUHLvHICnwijyX8ROGoiUPwNKZ7915MeYfCE",
"disabled": True, "disabled": True,
}, },
} }
@ -58,7 +58,7 @@ class UserInDB(User):
hashed_password: str hashed_password: str
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") password_hash = PasswordHash.recommended()
oauth2_scheme = OAuth2PasswordBearer( oauth2_scheme = OAuth2PasswordBearer(
tokenUrl="token", tokenUrl="token",
@ -69,11 +69,11 @@ app = FastAPI()
def verify_password(plain_password, hashed_password): 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): def get_password_hash(password):
return pwd_context.hash(password) return password_hash.hash(password)
def get_user(db, username: str): def get_user(db, username: str):

2
pyproject.toml

@ -172,8 +172,6 @@ junit_family = "xunit2"
filterwarnings = [ filterwarnings = [
"error", "error",
'ignore:starlette.middleware.wsgi is deprecated and will be removed in a future release\..*:DeprecationWarning:starlette', '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 # 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", "ignore:You seem to already have a custom.*:RuntimeWarning:trio",
# TODO: remove after upgrading SQLAlchemy to a version that includes the following changes # TODO: remove after upgrading SQLAlchemy to a version that includes the following changes

2
requirements-tests.txt

@ -9,7 +9,7 @@ flask >=1.1.2,<4.0.0
anyio[trio] >=3.2.1,<5.0.0 anyio[trio] >=3.2.1,<5.0.0
PyJWT==2.9.0 PyJWT==2.9.0
pyyaml >=5.3.1,<7.0.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 inline-snapshot>=0.21.1
# types # types
types-ujson ==5.10.0.20240515 types-ujson ==5.10.0.20240515

Loading…
Cancel
Save