From 1a3936b37704c8035acd07d72a812fec99bc40f3 Mon Sep 17 00:00:00 2001 From: Adit Soni <51469232+AditSoni@users.noreply.github.com> Date: Thu, 22 May 2025 16:58:01 +0530 Subject: [PATCH 1/3] Updated http.py: Fixed response status code in case of missing auth creds Replaced 403 status code with 401 ; as 403 represents correct authentication but incorrect authorization. And the cases here are about missing creds in auth headers so 401 makes more sense. The response body is displaying the correct message but the response status code was 403. --- fastapi/security/http.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fastapi/security/http.py b/fastapi/security/http.py index 9ab2df3c9..b56f5310f 100644 --- a/fastapi/security/http.py +++ b/fastapi/security/http.py @@ -9,7 +9,7 @@ from fastapi.security.base import SecurityBase from fastapi.security.utils import get_authorization_scheme_param from pydantic import BaseModel from starlette.requests import Request -from starlette.status import HTTP_401_UNAUTHORIZED, HTTP_403_FORBIDDEN +from starlette.status import HTTP_401_UNAUTHORIZED from typing_extensions import Annotated, Doc @@ -87,7 +87,7 @@ class HTTPBase(SecurityBase): if not (authorization and scheme and credentials): if self.auto_error: raise HTTPException( - status_code=HTTP_403_FORBIDDEN, detail="Not authenticated" + status_code=HTTP_401_UNAUTHORIZED, detail="Not authenticated" ) else: return None @@ -306,14 +306,14 @@ class HTTPBearer(HTTPBase): if not (authorization and scheme and credentials): if self.auto_error: raise HTTPException( - status_code=HTTP_403_FORBIDDEN, detail="Not authenticated" + status_code=HTTP_401_UNAUTHORIZED, detail="Not authenticated" ) else: return None if scheme.lower() != "bearer": if self.auto_error: raise HTTPException( - status_code=HTTP_403_FORBIDDEN, + status_code=HTTP_401_UNAUTHORIZED, detail="Invalid authentication credentials", ) else: @@ -408,14 +408,14 @@ class HTTPDigest(HTTPBase): if not (authorization and scheme and credentials): if self.auto_error: raise HTTPException( - status_code=HTTP_403_FORBIDDEN, detail="Not authenticated" + status_code=HTTP_401_UNAUTHORIZED, detail="Not authenticated" ) else: return None if scheme.lower() != "digest": if self.auto_error: raise HTTPException( - status_code=HTTP_403_FORBIDDEN, + status_code=HTTP_401_UNAUTHORIZED, detail="Invalid authentication credentials", ) else: From 36d21764f04cfea5ab71ca1b3f37a018acbe925a Mon Sep 17 00:00:00 2001 From: Adit Soni Date: Thu, 22 May 2025 17:29:18 +0530 Subject: [PATCH 2/3] Updated test cases to match expected 401 response status code. --- tests/test_security_http_base.py | 2 +- tests/test_security_http_base_description.py | 2 +- tests/test_security_http_bearer.py | 4 ++-- tests/test_security_http_bearer_description.py | 4 ++-- tests/test_security_http_digest.py | 4 ++-- tests/test_security_http_digest_description.py | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/test_security_http_base.py b/tests/test_security_http_base.py index 51928bafd..cf9252c5a 100644 --- a/tests/test_security_http_base.py +++ b/tests/test_security_http_base.py @@ -23,7 +23,7 @@ def test_security_http_base(): def test_security_http_base_no_credentials(): response = client.get("/users/me") - assert response.status_code == 403, response.text + assert response.status_code == 401, response.text assert response.json() == {"detail": "Not authenticated"} diff --git a/tests/test_security_http_base_description.py b/tests/test_security_http_base_description.py index bc79f3242..06ae9299a 100644 --- a/tests/test_security_http_base_description.py +++ b/tests/test_security_http_base_description.py @@ -23,7 +23,7 @@ def test_security_http_base(): def test_security_http_base_no_credentials(): response = client.get("/users/me") - assert response.status_code == 403, response.text + assert response.status_code == 401, response.text assert response.json() == {"detail": "Not authenticated"} diff --git a/tests/test_security_http_bearer.py b/tests/test_security_http_bearer.py index 5b9e2d691..044fe8d78 100644 --- a/tests/test_security_http_bearer.py +++ b/tests/test_security_http_bearer.py @@ -23,13 +23,13 @@ def test_security_http_bearer(): def test_security_http_bearer_no_credentials(): response = client.get("/users/me") - assert response.status_code == 403, response.text + assert response.status_code == 401, response.text assert response.json() == {"detail": "Not authenticated"} def test_security_http_bearer_incorrect_scheme_credentials(): response = client.get("/users/me", headers={"Authorization": "Basic notreally"}) - assert response.status_code == 403, response.text + assert response.status_code == 401, response.text assert response.json() == {"detail": "Invalid authentication credentials"} diff --git a/tests/test_security_http_bearer_description.py b/tests/test_security_http_bearer_description.py index 2f11c3a14..7be5858c9 100644 --- a/tests/test_security_http_bearer_description.py +++ b/tests/test_security_http_bearer_description.py @@ -23,13 +23,13 @@ def test_security_http_bearer(): def test_security_http_bearer_no_credentials(): response = client.get("/users/me") - assert response.status_code == 403, response.text + assert response.status_code == 401, response.text assert response.json() == {"detail": "Not authenticated"} def test_security_http_bearer_incorrect_scheme_credentials(): response = client.get("/users/me", headers={"Authorization": "Basic notreally"}) - assert response.status_code == 403, response.text + assert response.status_code == 401, response.text assert response.json() == {"detail": "Invalid authentication credentials"} diff --git a/tests/test_security_http_digest.py b/tests/test_security_http_digest.py index 133d35763..11b69d670 100644 --- a/tests/test_security_http_digest.py +++ b/tests/test_security_http_digest.py @@ -23,7 +23,7 @@ def test_security_http_digest(): def test_security_http_digest_no_credentials(): response = client.get("/users/me") - assert response.status_code == 403, response.text + assert response.status_code == 401, response.text assert response.json() == {"detail": "Not authenticated"} @@ -31,7 +31,7 @@ def test_security_http_digest_incorrect_scheme_credentials(): response = client.get( "/users/me", headers={"Authorization": "Other invalidauthorization"} ) - assert response.status_code == 403, response.text + assert response.status_code == 401, response.text assert response.json() == {"detail": "Invalid authentication credentials"} diff --git a/tests/test_security_http_digest_description.py b/tests/test_security_http_digest_description.py index 4e31a0c00..e71bc312f 100644 --- a/tests/test_security_http_digest_description.py +++ b/tests/test_security_http_digest_description.py @@ -23,7 +23,7 @@ def test_security_http_digest(): def test_security_http_digest_no_credentials(): response = client.get("/users/me") - assert response.status_code == 403, response.text + assert response.status_code == 401, response.text assert response.json() == {"detail": "Not authenticated"} @@ -31,7 +31,7 @@ def test_security_http_digest_incorrect_scheme_credentials(): response = client.get( "/users/me", headers={"Authorization": "Other invalidauthorization"} ) - assert response.status_code == 403, response.text + assert response.status_code == 401, response.text assert response.json() == {"detail": "Invalid authentication credentials"} From f5e7089cb3f4732237aed654aba0ce0a49fb103e Mon Sep 17 00:00:00 2001 From: Adit Soni Date: Thu, 22 May 2025 17:42:21 +0530 Subject: [PATCH 3/3] Fixed other places as well which had incorrect response status codes. --- fastapi/security/api_key.py | 4 ++-- fastapi/security/oauth2.py | 4 ++-- fastapi/security/open_id_connect_url.py | 4 ++-- tests/test_security_api_key_cookie.py | 2 +- tests/test_security_api_key_cookie_description.py | 2 +- tests/test_security_api_key_header.py | 2 +- tests/test_security_api_key_header_description.py | 2 +- tests/test_security_api_key_query.py | 2 +- tests/test_security_api_key_query_description.py | 2 +- tests/test_security_oauth2.py | 2 +- tests/test_security_openid_connect.py | 2 +- tests/test_security_openid_connect_description.py | 2 +- 12 files changed, 15 insertions(+), 15 deletions(-) diff --git a/fastapi/security/api_key.py b/fastapi/security/api_key.py index 70c2dca8a..cb0e12726 100644 --- a/fastapi/security/api_key.py +++ b/fastapi/security/api_key.py @@ -4,7 +4,7 @@ from fastapi.openapi.models import APIKey, APIKeyIn from fastapi.security.base import SecurityBase from starlette.exceptions import HTTPException from starlette.requests import Request -from starlette.status import HTTP_403_FORBIDDEN +from starlette.status import HTTP_401_UNAUTHORIZED from typing_extensions import Annotated, Doc @@ -14,7 +14,7 @@ class APIKeyBase(SecurityBase): if not api_key: if auto_error: raise HTTPException( - status_code=HTTP_403_FORBIDDEN, detail="Not authenticated" + status_code=HTTP_401_UNAUTHORIZED, detail="Not authenticated" ) return None return api_key diff --git a/fastapi/security/oauth2.py b/fastapi/security/oauth2.py index 5ffad5986..08a0e030d 100644 --- a/fastapi/security/oauth2.py +++ b/fastapi/security/oauth2.py @@ -7,7 +7,7 @@ from fastapi.param_functions import Form from fastapi.security.base import SecurityBase from fastapi.security.utils import get_authorization_scheme_param from starlette.requests import Request -from starlette.status import HTTP_401_UNAUTHORIZED, HTTP_403_FORBIDDEN +from starlette.status import HTTP_401_UNAUTHORIZED # TODO: import from typing when deprecating Python 3.9 from typing_extensions import Annotated, Doc @@ -381,7 +381,7 @@ class OAuth2(SecurityBase): if not authorization: if self.auto_error: raise HTTPException( - status_code=HTTP_403_FORBIDDEN, detail="Not authenticated" + status_code=HTTP_401_UNAUTHORIZED, detail="Not authenticated" ) else: return None diff --git a/fastapi/security/open_id_connect_url.py b/fastapi/security/open_id_connect_url.py index c8cceb911..7fd240ec5 100644 --- a/fastapi/security/open_id_connect_url.py +++ b/fastapi/security/open_id_connect_url.py @@ -4,7 +4,7 @@ from fastapi.openapi.models import OpenIdConnect as OpenIdConnectModel from fastapi.security.base import SecurityBase from starlette.exceptions import HTTPException from starlette.requests import Request -from starlette.status import HTTP_403_FORBIDDEN +from starlette.status import HTTP_401_UNAUTHORIZED from typing_extensions import Annotated, Doc @@ -77,7 +77,7 @@ class OpenIdConnect(SecurityBase): if not authorization: if self.auto_error: raise HTTPException( - status_code=HTTP_403_FORBIDDEN, detail="Not authenticated" + status_code=HTTP_401_UNAUTHORIZED, detail="Not authenticated" ) else: return None diff --git a/tests/test_security_api_key_cookie.py b/tests/test_security_api_key_cookie.py index 4ddb8e2ee..419332a15 100644 --- a/tests/test_security_api_key_cookie.py +++ b/tests/test_security_api_key_cookie.py @@ -32,7 +32,7 @@ def test_security_api_key(): def test_security_api_key_no_key(): client = TestClient(app) response = client.get("/users/me") - assert response.status_code == 403, response.text + assert response.status_code == 401, response.text assert response.json() == {"detail": "Not authenticated"} diff --git a/tests/test_security_api_key_cookie_description.py b/tests/test_security_api_key_cookie_description.py index d99d616e0..080d3b22a 100644 --- a/tests/test_security_api_key_cookie_description.py +++ b/tests/test_security_api_key_cookie_description.py @@ -32,7 +32,7 @@ def test_security_api_key(): def test_security_api_key_no_key(): client = TestClient(app) response = client.get("/users/me") - assert response.status_code == 403, response.text + assert response.status_code == 401, response.text assert response.json() == {"detail": "Not authenticated"} diff --git a/tests/test_security_api_key_header.py b/tests/test_security_api_key_header.py index 1ff883703..082326224 100644 --- a/tests/test_security_api_key_header.py +++ b/tests/test_security_api_key_header.py @@ -33,7 +33,7 @@ def test_security_api_key(): def test_security_api_key_no_key(): response = client.get("/users/me") - assert response.status_code == 403, response.text + assert response.status_code == 401, response.text assert response.json() == {"detail": "Not authenticated"} diff --git a/tests/test_security_api_key_header_description.py b/tests/test_security_api_key_header_description.py index 27f9d0f29..0ad1d22c8 100644 --- a/tests/test_security_api_key_header_description.py +++ b/tests/test_security_api_key_header_description.py @@ -33,7 +33,7 @@ def test_security_api_key(): def test_security_api_key_no_key(): response = client.get("/users/me") - assert response.status_code == 403, response.text + assert response.status_code == 401, response.text assert response.json() == {"detail": "Not authenticated"} diff --git a/tests/test_security_api_key_query.py b/tests/test_security_api_key_query.py index dc7a0a621..f07a38c7c 100644 --- a/tests/test_security_api_key_query.py +++ b/tests/test_security_api_key_query.py @@ -33,7 +33,7 @@ def test_security_api_key(): def test_security_api_key_no_key(): response = client.get("/users/me") - assert response.status_code == 403, response.text + assert response.status_code == 401, response.text assert response.json() == {"detail": "Not authenticated"} diff --git a/tests/test_security_api_key_query_description.py b/tests/test_security_api_key_query_description.py index 35dc7743a..137494654 100644 --- a/tests/test_security_api_key_query_description.py +++ b/tests/test_security_api_key_query_description.py @@ -33,7 +33,7 @@ def test_security_api_key(): def test_security_api_key_no_key(): response = client.get("/users/me") - assert response.status_code == 403, response.text + assert response.status_code == 401, response.text assert response.json() == {"detail": "Not authenticated"} diff --git a/tests/test_security_oauth2.py b/tests/test_security_oauth2.py index 2b7e3457a..1c5f070bb 100644 --- a/tests/test_security_oauth2.py +++ b/tests/test_security_oauth2.py @@ -56,7 +56,7 @@ def test_security_oauth2_password_other_header(): def test_security_oauth2_password_bearer_no_header(): response = client.get("/users/me") - assert response.status_code == 403, response.text + assert response.status_code == 401, response.text assert response.json() == {"detail": "Not authenticated"} diff --git a/tests/test_security_openid_connect.py b/tests/test_security_openid_connect.py index 1e322e640..8a548e5c7 100644 --- a/tests/test_security_openid_connect.py +++ b/tests/test_security_openid_connect.py @@ -39,7 +39,7 @@ def test_security_oauth2_password_other_header(): def test_security_oauth2_password_bearer_no_header(): response = client.get("/users/me") - assert response.status_code == 403, response.text + assert response.status_code == 401, response.text assert response.json() == {"detail": "Not authenticated"} diff --git a/tests/test_security_openid_connect_description.py b/tests/test_security_openid_connect_description.py index 44cf57f86..dc4bc649a 100644 --- a/tests/test_security_openid_connect_description.py +++ b/tests/test_security_openid_connect_description.py @@ -41,7 +41,7 @@ def test_security_oauth2_password_other_header(): def test_security_oauth2_password_bearer_no_header(): response = client.get("/users/me") - assert response.status_code == 403, response.text + assert response.status_code == 401, response.text assert response.json() == {"detail": "Not authenticated"}