From 4fb016ccfdf45366fb53e56da4c2ceeceaa5cb19 Mon Sep 17 00:00:00 2001 From: Sudhakar Pallaprolu Date: Tue, 23 Dec 2025 17:11:00 -0600 Subject: [PATCH] fix: Require realm in HTTPBasic to comply with RFC 7617 (#13471) --- docs_src/security/tutorial006_an_py39.py | 2 +- docs_src/security/tutorial006_py39.py | 2 +- docs_src/security/tutorial007_an_py39.py | 4 ++-- docs_src/security/tutorial007_py39.py | 4 ++-- fastapi/security/http.py | 8 +++----- tests/test_security_http_basic_optional.py | 6 +++--- tests/test_tutorial/test_security/test_tutorial006.py | 6 +++--- 7 files changed, 15 insertions(+), 17 deletions(-) diff --git a/docs_src/security/tutorial006_an_py39.py b/docs_src/security/tutorial006_an_py39.py index 03c696a4b..721716a15 100644 --- a/docs_src/security/tutorial006_an_py39.py +++ b/docs_src/security/tutorial006_an_py39.py @@ -5,7 +5,7 @@ from fastapi.security import HTTPBasic, HTTPBasicCredentials app = FastAPI() -security = HTTPBasic() +security = HTTPBasic(realm="simple") @app.get("/users/me") diff --git a/docs_src/security/tutorial006_py39.py b/docs_src/security/tutorial006_py39.py index 29121ffd6..5b9e9a8e8 100644 --- a/docs_src/security/tutorial006_py39.py +++ b/docs_src/security/tutorial006_py39.py @@ -3,7 +3,7 @@ from fastapi.security import HTTPBasic, HTTPBasicCredentials app = FastAPI() -security = HTTPBasic() +security = HTTPBasic(realm="simple") @app.get("/users/me") diff --git a/docs_src/security/tutorial007_an_py39.py b/docs_src/security/tutorial007_an_py39.py index 87ef98657..0dd34a335 100644 --- a/docs_src/security/tutorial007_an_py39.py +++ b/docs_src/security/tutorial007_an_py39.py @@ -6,7 +6,7 @@ from fastapi.security import HTTPBasic, HTTPBasicCredentials app = FastAPI() -security = HTTPBasic() +security = HTTPBasic(realm="simple") def get_current_username( @@ -26,7 +26,7 @@ def get_current_username( raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect username or password", - headers={"WWW-Authenticate": "Basic"}, + headers={"WWW-Authenticate": 'Basic realm="simple"'}, ) return credentials.username diff --git a/docs_src/security/tutorial007_py39.py b/docs_src/security/tutorial007_py39.py index ac816eb0c..107cee39f 100644 --- a/docs_src/security/tutorial007_py39.py +++ b/docs_src/security/tutorial007_py39.py @@ -5,7 +5,7 @@ from fastapi.security import HTTPBasic, HTTPBasicCredentials app = FastAPI() -security = HTTPBasic() +security = HTTPBasic(realm="simple") def get_current_username(credentials: HTTPBasicCredentials = Depends(security)): @@ -23,7 +23,7 @@ def get_current_username(credentials: HTTPBasicCredentials = Depends(security)): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect username or password", - headers={"WWW-Authenticate": "Basic"}, + headers={"WWW-Authenticate": 'Basic realm="simple"'}, ) return credentials.username diff --git a/fastapi/security/http.py b/fastapi/security/http.py index b4c3bc6d8..9ef32895b 100644 --- a/fastapi/security/http.py +++ b/fastapi/security/http.py @@ -153,13 +153,13 @@ class HTTPBasic(HTTPBase): ), ] = None, realm: Annotated[ - Optional[str], + str, Doc( """ HTTP Basic authentication realm. """ ), - ] = None, + ], description: Annotated[ Optional[str], Doc( @@ -197,9 +197,7 @@ class HTTPBasic(HTTPBase): self.auto_error = auto_error def make_authenticate_headers(self) -> dict[str, str]: - if self.realm: - return {"WWW-Authenticate": f'Basic realm="{self.realm}"'} - return {"WWW-Authenticate": "Basic"} + return {"WWW-Authenticate": f'Basic realm="{self.realm}"'} async def __call__( # type: ignore self, request: Request diff --git a/tests/test_security_http_basic_optional.py b/tests/test_security_http_basic_optional.py index 7071f381a..8ab57a8b7 100644 --- a/tests/test_security_http_basic_optional.py +++ b/tests/test_security_http_basic_optional.py @@ -7,7 +7,7 @@ from fastapi.testclient import TestClient app = FastAPI() -security = HTTPBasic(auto_error=False) +security = HTTPBasic(realm="simple", auto_error=False) @app.get("/users/me") @@ -37,7 +37,7 @@ def test_security_http_basic_invalid_credentials(): "/users/me", headers={"Authorization": "Basic notabase64token"} ) assert response.status_code == 401, response.text - assert response.headers["WWW-Authenticate"] == "Basic" + assert response.headers["WWW-Authenticate"] == 'Basic realm="simple"' assert response.json() == {"detail": "Not authenticated"} @@ -46,7 +46,7 @@ def test_security_http_basic_non_basic_credentials(): auth_header = f"Basic {payload}" response = client.get("/users/me", headers={"Authorization": auth_header}) assert response.status_code == 401, response.text - assert response.headers["WWW-Authenticate"] == "Basic" + assert response.headers["WWW-Authenticate"] == 'Basic realm="simple"' assert response.json() == {"detail": "Not authenticated"} diff --git a/tests/test_tutorial/test_security/test_tutorial006.py b/tests/test_tutorial/test_security/test_tutorial006.py index a4b3104bb..792302ca9 100644 --- a/tests/test_tutorial/test_security/test_tutorial006.py +++ b/tests/test_tutorial/test_security/test_tutorial006.py @@ -29,7 +29,7 @@ def test_security_http_basic_no_credentials(client: TestClient): response = client.get("/users/me") assert response.json() == {"detail": "Not authenticated"} assert response.status_code == 401, response.text - assert response.headers["WWW-Authenticate"] == "Basic" + assert response.headers["WWW-Authenticate"] == 'Basic realm="simple"' def test_security_http_basic_invalid_credentials(client: TestClient): @@ -37,7 +37,7 @@ def test_security_http_basic_invalid_credentials(client: TestClient): "/users/me", headers={"Authorization": "Basic notabase64token"} ) assert response.status_code == 401, response.text - assert response.headers["WWW-Authenticate"] == "Basic" + assert response.headers["WWW-Authenticate"] == 'Basic realm="simple"' assert response.json() == {"detail": "Not authenticated"} @@ -46,7 +46,7 @@ def test_security_http_basic_non_basic_credentials(client: TestClient): auth_header = f"Basic {payload}" response = client.get("/users/me", headers={"Authorization": auth_header}) assert response.status_code == 401, response.text - assert response.headers["WWW-Authenticate"] == "Basic" + assert response.headers["WWW-Authenticate"] == 'Basic realm="simple"' assert response.json() == {"detail": "Not authenticated"}