]> git.ipfire.org Git - thirdparty/fastapi/fastapi.git/commitdiff
Use 401 instead of 403 in security classes 401-instead-of-403 13514/head
authorMarcelo Trylesinski <marcelotryle@gmail.com>
Thu, 20 Mar 2025 12:19:07 +0000 (12:19 +0000)
committerMarcelo Trylesinski <marcelotryle@gmail.com>
Thu, 20 Mar 2025 12:19:07 +0000 (12:19 +0000)
fastapi/security/http.py
fastapi/security/oauth2.py

index 9ab2df3c98e1e08a920de9439e78ecd65c2daada..4054528b4c20b91012f4ea970efb8e6f0e184ece 100644 (file)
@@ -303,18 +303,23 @@ class HTTPBearer(HTTPBase):
     ) -> Optional[HTTPAuthorizationCredentials]:
         authorization = request.headers.get("Authorization")
         scheme, credentials = get_authorization_scheme_param(authorization)
+        # All fields besides the scheme are optional, as per https://www.rfc-editor.org/rfc/rfc6750.html#section-3.
+        unauthorized_headers = {"WWW-Authenticate": "Bearer"}
         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",
+                    headers=unauthorized_headers,
                 )
             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",
+                    headers=unauthorized_headers,
                 )
             else:
                 return None
@@ -405,18 +410,23 @@ class HTTPDigest(HTTPBase):
     ) -> Optional[HTTPAuthorizationCredentials]:
         authorization = request.headers.get("Authorization")
         scheme, credentials = get_authorization_scheme_param(authorization)
+        # All fields besides the scheme are optional, as per https://datatracker.ietf.org/doc/html/rfc7616#section-3.3.
+        unauthorized_headers = {"WWW-Authenticate": "Digest"}
         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",
+                    headers=unauthorized_headers,
                 )
             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",
+                    headers=unauthorized_headers,
                 )
             else:
                 return None
index 5ffad59862ba942a26dc27ce77c66578659918d3..9efa65a4cf927bdfd24406b7fb8a9dfd2b1a4bf4 100644 (file)
@@ -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,9 @@ 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",
+                    headers={"WWW-Authenticate": "Bearer"},
                 )
             else:
                 return None