diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 94016ad31d..8b24c8c0cd 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -14,7 +14,7 @@ repos:
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/astral-sh/ruff-pre-commit
- rev: v0.13.0
+ rev: v0.13.1
hooks:
- id: ruff
args:
diff --git a/docs/de/docs/tutorial/cookie-params.md b/docs/de/docs/tutorial/cookie-params.md
index 9f50c7ae58..81a753211a 100644
--- a/docs/de/docs/tutorial/cookie-params.md
+++ b/docs/de/docs/tutorial/cookie-params.md
@@ -30,6 +30,16 @@ Um Cookies zu deklarieren, müssen Sie `Cookie` verwenden, da die Parameter sons
///
+/// info | Info
+
+Beachten Sie, dass **Browser Cookies auf besondere Weise und hinter den Kulissen handhaben** und **JavaScript** **nicht** ohne Weiteres erlauben, auf sie zuzugreifen.
+
+Wenn Sie zur **API-Dokumentations-UI** unter `/docs` gehen, können Sie die **Dokumentation** zu Cookies für Ihre *Pfadoperationen* sehen.
+
+Aber selbst wenn Sie die **Daten ausfüllen** und auf „Execute“ klicken, da die Dokumentations-UI mit **JavaScript** arbeitet, werden die Cookies nicht gesendet, und Sie sehen eine **Fehler**-Meldung, als hätten Sie keine Werte eingegeben.
+
+///
+
## Zusammenfassung { #recap }
Deklarieren Sie Cookies mit `Cookie` und verwenden Sie dabei das gleiche allgemeine Muster wie bei `Query` und `Path`.
diff --git a/docs/de/docs/tutorial/path-params-numeric-validations.md b/docs/de/docs/tutorial/path-params-numeric-validations.md
index 36f4660594..5b74749447 100644
--- a/docs/de/docs/tutorial/path-params-numeric-validations.md
+++ b/docs/de/docs/tutorial/path-params-numeric-validations.md
@@ -54,18 +54,8 @@ Für **FastAPI** spielt es keine Rolle. Es erkennt die Parameter anhand ihrer Na
Sie können Ihre Funktion also so deklarieren:
-//// tab | Python 3.8 nicht annotiert
-
-/// tip | Tipp
-
-Bevorzugen Sie die `Annotated`-Version, falls möglich.
-
-///
-
{* ../../docs_src/path_params_numeric_validations/tutorial002.py hl[7] *}
-////
-
Aber bedenken Sie, dass Sie dieses Problem nicht haben, wenn Sie `Annotated` verwenden, da es nicht darauf ankommt, dass Sie keine Funktionsparameter-Defaultwerte für `Query()` oder `Path()` verwenden.
{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py39.py *}
diff --git a/docs/en/docs/contributing.md b/docs/en/docs/contributing.md
index 2583fd1fb9..ae99059f41 100644
--- a/docs/en/docs/contributing.md
+++ b/docs/en/docs/contributing.md
@@ -10,10 +10,12 @@ If you already cloned the
```console
@@ -24,6 +26,24 @@ $ pip install -r requirements.txt
+////
+
+//// tab | `uv`
+
+If you have `uv`:
+
+
+
+```console
+$ uv pip install -r requirements.txt
+
+---> 100%
+```
+
+
+
+////
+
It will install all the dependencies and your local FastAPI in your local environment.
### Using your local FastAPI
diff --git a/docs/en/docs/css/custom.css b/docs/en/docs/css/custom.css
index b192f6123a..a38df772f9 100644
--- a/docs/en/docs/css/custom.css
+++ b/docs/en/docs/css/custom.css
@@ -102,7 +102,15 @@ a.announce-link:hover {
align-items: center;
}
-.announce-wrapper div.item {
+.announce-wrapper #announce-left div.item {
+ display: none;
+}
+
+.announce-wrapper #announce-right {
+ display: none;
+}
+
+.announce-wrapper #announce-right div.item {
display: none;
}
@@ -112,7 +120,7 @@ a.announce-link:hover {
top: -10px;
right: 0;
font-size: 0.5rem;
- color: #999;
+ color: #e6e6e6;
background-color: #666;
border-radius: 10px;
padding: 0 10px;
diff --git a/docs/en/docs/how-to/conditional-openapi.md b/docs/en/docs/how-to/conditional-openapi.md
index 833123e6a7..e5893e584b 100644
--- a/docs/en/docs/how-to/conditional-openapi.md
+++ b/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.
* 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.
diff --git a/docs/en/docs/js/custom.js b/docs/en/docs/js/custom.js
index 4c0ada312e..48e95901d5 100644
--- a/docs/en/docs/js/custom.js
+++ b/docs/en/docs/js/custom.js
@@ -135,10 +135,43 @@ async function showRandomAnnouncement(groupId, timeInterval) {
}
}
+function handleSponsorImages() {
+ const announceRight = document.getElementById('announce-right');
+ if(!announceRight) return;
+
+ const sponsorImages = document.querySelectorAll('.sponsor-image');
+
+ const imagePromises = Array.from(sponsorImages).map(img => {
+ return new Promise((resolve, reject) => {
+ if (img.complete && img.naturalHeight !== 0) {
+ resolve();
+ } else {
+ img.addEventListener('load', () => {
+ if (img.naturalHeight !== 0) {
+ resolve();
+ } else {
+ reject();
+ }
+ });
+ img.addEventListener('error', reject);
+ }
+ });
+ });
+
+ Promise.all(imagePromises)
+ .then(() => {
+ announceRight.style.display = 'block';
+ showRandomAnnouncement('announce-right', 10000);
+ })
+ .catch(() => {
+ // do nothing
+ });
+}
+
async function main() {
setupTermynal();
showRandomAnnouncement('announce-left', 5000)
- showRandomAnnouncement('announce-right', 10000)
+ handleSponsorImages();
}
document$.subscribe(() => {
main()
diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md
index 25744044e9..3eeb976d71 100644
--- a/docs/en/docs/release-notes.md
+++ b/docs/en/docs/release-notes.md
@@ -7,6 +7,23 @@ hide:
## Latest Changes
+### Docs
+
+* 📝 Update `tutorial/security/oauth2-jwt/` to use `pwdlib` with Argon2 instead of `passlib`. PR [#13917](https://github.com/fastapi/fastapi/pull/13917) by [@Neizvestnyj](https://github.com/Neizvestnyj).
+* ✏️ Fix typos in OAuth2 password request forms. PR [#14112](https://github.com/fastapi/fastapi/pull/14112) by [@alv2017](https://github.com/alv2017).
+* 📝 Update contributing guidelines for installing requirements. PR [#14095](https://github.com/fastapi/fastapi/pull/14095) by [@alejsdev](https://github.com/alejsdev).
+
+### Translations
+
+* 🌐 Sync German docs. PR [#14098](https://github.com/fastapi/fastapi/pull/14098) by [@nilslindemann](https://github.com/nilslindemann).
+
+### Internal
+
+* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#14103](https://github.com/fastapi/fastapi/pull/14103) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).
+* ♻️ Refactor sponsor image handling. PR [#14102](https://github.com/fastapi/fastapi/pull/14102) by [@alejsdev](https://github.com/alejsdev).
+* 🐛 Fix sponsor display issue by hiding element on image error. PR [#14097](https://github.com/fastapi/fastapi/pull/14097) by [@alejsdev](https://github.com/alejsdev).
+* 🐛 Hide sponsor badge when sponsor image is not displayed. PR [#14096](https://github.com/fastapi/fastapi/pull/14096) by [@alejsdev](https://github.com/alejsdev).
+
## 0.117.1
### Fixes
diff --git a/docs/en/docs/tutorial/security/oauth2-jwt.md b/docs/en/docs/tutorial/security/oauth2-jwt.md
index e9ae0db680..95baf871c1 100644
--- a/docs/en/docs/tutorial/security/oauth2-jwt.md
+++ b/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).
-## 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:
```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.
diff --git a/docs_src/security/tutorial004.py b/docs_src/security/tutorial004.py
index 2225896186..130dc699a0 100644
--- a/docs_src/security/tutorial004.py
+++ b/docs_src/security/tutorial004.py
@@ -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):
diff --git a/docs_src/security/tutorial004_an.py b/docs_src/security/tutorial004_an.py
index e2221cd399..018234e300 100644
--- a/docs_src/security/tutorial004_an.py
+++ b/docs_src/security/tutorial004_an.py
@@ -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):
diff --git a/docs_src/security/tutorial004_an_py310.py b/docs_src/security/tutorial004_an_py310.py
index a3f74fc0e5..18ea96bc5c 100644
--- a/docs_src/security/tutorial004_an_py310.py
+++ b/docs_src/security/tutorial004_an_py310.py
@@ -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):
diff --git a/docs_src/security/tutorial004_an_py39.py b/docs_src/security/tutorial004_an_py39.py
index b33d677ed1..d3fd29e5a5 100644
--- a/docs_src/security/tutorial004_an_py39.py
+++ b/docs_src/security/tutorial004_an_py39.py
@@ -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):
diff --git a/docs_src/security/tutorial004_py310.py b/docs_src/security/tutorial004_py310.py
index d46ce26bf8..cd1dcff460 100644
--- a/docs_src/security/tutorial004_py310.py
+++ b/docs_src/security/tutorial004_py310.py
@@ -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):
diff --git a/docs_src/security/tutorial005.py b/docs_src/security/tutorial005.py
index 447dacb372..fdd73bcd8a 100644
--- a/docs_src/security/tutorial005.py
+++ b/docs_src/security/tutorial005.py
@@ -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):
diff --git a/docs_src/security/tutorial005_an.py b/docs_src/security/tutorial005_an.py
index d2c4fe9b8d..e1d7b4f62a 100644
--- a/docs_src/security/tutorial005_an.py
+++ b/docs_src/security/tutorial005_an.py
@@ -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):
diff --git a/docs_src/security/tutorial005_an_py310.py b/docs_src/security/tutorial005_an_py310.py
index e3527370d8..df55951c07 100644
--- a/docs_src/security/tutorial005_an_py310.py
+++ b/docs_src/security/tutorial005_an_py310.py
@@ -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):
diff --git a/docs_src/security/tutorial005_an_py39.py b/docs_src/security/tutorial005_an_py39.py
index 3dc3140c3b..983c1c22cf 100644
--- a/docs_src/security/tutorial005_an_py39.py
+++ b/docs_src/security/tutorial005_an_py39.py
@@ -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):
diff --git a/docs_src/security/tutorial005_py310.py b/docs_src/security/tutorial005_py310.py
index 3fc15212bd..d08e2c59f3 100644
--- a/docs_src/security/tutorial005_py310.py
+++ b/docs_src/security/tutorial005_py310.py
@@ -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):
diff --git a/docs_src/security/tutorial005_py39.py b/docs_src/security/tutorial005_py39.py
index f9aed0a42a..5bde47ef47 100644
--- a/docs_src/security/tutorial005_py39.py
+++ b/docs_src/security/tutorial005_py39.py
@@ -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):
diff --git a/fastapi/security/oauth2.py b/fastapi/security/oauth2.py
index 88e394db11..fdedbc2dad 100644
--- a/fastapi/security/oauth2.py
+++ b/fastapi/security/oauth2.py
@@ -89,7 +89,7 @@ class OAuth2PasswordRequestForm:
Doc(
"""
`password` string. The OAuth2 spec requires the exact field name
- `password".
+ `password`.
"""
),
],
@@ -243,7 +243,7 @@ class OAuth2PasswordRequestFormStrict(OAuth2PasswordRequestForm):
Doc(
"""
`password` string. The OAuth2 spec requires the exact field name
- `password".
+ `password`.
"""
),
],
diff --git a/pyproject.toml b/pyproject.toml
index fbfdea79a4..41ef1eb768 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -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
diff --git a/requirements-tests.txt b/requirements-tests.txt
index e87a42162c..53ec28d2ea 100644
--- a/requirements-tests.txt
+++ b/requirements-tests.txt
@@ -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