From 1de6760d943b9b94c59c335ab8091f00a4570087 Mon Sep 17 00:00:00 2001 From: Maxime Grenu Date: Sun, 8 Mar 2026 22:46:15 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20Add=20missing=20password=20forma?= =?UTF-8?q?t=20to=20OAuth2PasswordRequestFormStrict?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The strict variant was missing `json_schema_extra={"format": "password"}` on its `password` and `client_secret` fields, unlike the non-strict `OAuth2PasswordRequestForm` which already had them. This meant the Swagger UI rendered those fields as plain text inputs instead of masked password inputs when using the strict form. Signed-off-by: cluster2600 Signed-off-by: Maxime Grenu --- fastapi/security/oauth2.py | 4 ++-- tests/test_security_oauth2.py | 7 ++++++- tests/test_security_oauth2_optional.py | 7 ++++++- tests/test_security_oauth2_optional_description.py | 7 ++++++- 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/fastapi/security/oauth2.py b/fastapi/security/oauth2.py index 661043ce7b..a83c05b354 100644 --- a/fastapi/security/oauth2.py +++ b/fastapi/security/oauth2.py @@ -255,7 +255,7 @@ class OAuth2PasswordRequestFormStrict(OAuth2PasswordRequestForm): ], password: Annotated[ str, - Form(), + Form(json_schema_extra={"format": "password"}), Doc( """ `password` string. The OAuth2 spec requires the exact field name @@ -306,7 +306,7 @@ class OAuth2PasswordRequestFormStrict(OAuth2PasswordRequestForm): ] = None, client_secret: Annotated[ str | None, - Form(), + Form(json_schema_extra={"format": "password"}), Doc( """ If there's a `client_password` (and a `client_id`), they can be sent diff --git a/tests/test_security_oauth2.py b/tests/test_security_oauth2.py index 1c216e95d8..490b2264b6 100644 --- a/tests/test_security_oauth2.py +++ b/tests/test_security_oauth2.py @@ -213,7 +213,11 @@ def test_openapi_schema(): "type": "string", }, "username": {"title": "Username", "type": "string"}, - "password": {"title": "Password", "type": "string"}, + "password": { + "title": "Password", + "type": "string", + "format": "password", + }, "scope": { "title": "Scope", "type": "string", @@ -226,6 +230,7 @@ def test_openapi_schema(): "client_secret": { "title": "Client Secret", "anyOf": [{"type": "string"}, {"type": "null"}], + "format": "password", }, }, }, diff --git a/tests/test_security_oauth2_optional.py b/tests/test_security_oauth2_optional.py index a7eaf59443..c85f483b7c 100644 --- a/tests/test_security_oauth2_optional.py +++ b/tests/test_security_oauth2_optional.py @@ -214,7 +214,11 @@ def test_openapi_schema(): "type": "string", }, "username": {"title": "Username", "type": "string"}, - "password": {"title": "Password", "type": "string"}, + "password": { + "title": "Password", + "type": "string", + "format": "password", + }, "scope": { "title": "Scope", "type": "string", @@ -227,6 +231,7 @@ def test_openapi_schema(): "client_secret": { "title": "Client Secret", "anyOf": [{"type": "string"}, {"type": "null"}], + "format": "password", }, }, }, diff --git a/tests/test_security_oauth2_optional_description.py b/tests/test_security_oauth2_optional_description.py index 0918d352ea..4d995c955c 100644 --- a/tests/test_security_oauth2_optional_description.py +++ b/tests/test_security_oauth2_optional_description.py @@ -215,7 +215,11 @@ def test_openapi_schema(): "type": "string", }, "username": {"title": "Username", "type": "string"}, - "password": {"title": "Password", "type": "string"}, + "password": { + "title": "Password", + "type": "string", + "format": "password", + }, "scope": { "title": "Scope", "type": "string", @@ -228,6 +232,7 @@ def test_openapi_schema(): "client_secret": { "title": "Client Secret", "anyOf": [{"type": "string"}, {"type": "null"}], + "format": "password", }, }, },