Browse Source

fix: treat empty bearer token as missing credentials in OAuth2 helpers

Authorization: Bearer  (empty token after "Bearer") was returned as ""
instead of triggering the auto_error path. Both OAuth2PasswordBearer and
OAuth2AuthorizationCodeBearer only checked that the scheme was "bearer"
but not that the credential string was non-empty.

RFC 6750 Section 2.1 defines b64token = 1*(...) — at least one character
required. An empty credential is syntactically malformed and should be
treated the same as missing credentials: 401 with auto_error=True, None
with auto_error=False.

Fix: add `not param` to the guard condition in both __call__ methods.

Closes: https://github.com/fastapi/fastapi/discussions/15192
pull/15220/head
Subhash Dasyam 3 months ago
parent
commit
c5bd35c728
  1. 4
      fastapi/security/oauth2.py
  2. 6
      tests/test_security_oauth2_authorization_code_bearer.py
  3. 6
      tests/test_security_oauth2_password_bearer_optional.py

4
fastapi/security/oauth2.py

@ -536,7 +536,7 @@ class OAuth2PasswordBearer(OAuth2):
async def __call__(self, request: Request) -> str | None:
authorization = request.headers.get("Authorization")
scheme, param = get_authorization_scheme_param(authorization)
if not authorization or scheme.lower() != "bearer":
if not authorization or scheme.lower() != "bearer" or not param:
if self.auto_error:
raise self.make_not_authenticated_error()
else:
@ -642,7 +642,7 @@ class OAuth2AuthorizationCodeBearer(OAuth2):
async def __call__(self, request: Request) -> str | None:
authorization = request.headers.get("Authorization")
scheme, param = get_authorization_scheme_param(authorization)
if not authorization or scheme.lower() != "bearer":
if not authorization or scheme.lower() != "bearer" or not param:
if self.auto_error:
raise self.make_not_authenticated_error()
else:

6
tests/test_security_oauth2_authorization_code_bearer.py

@ -30,6 +30,12 @@ def test_incorrect_token():
assert response.json() == {"detail": "Not authenticated"}
def test_empty_bearer_token():
response = client.get("/items", headers={"Authorization": "Bearer "})
assert response.status_code == 401, response.text
assert response.json() == {"detail": "Not authenticated"}
def test_token():
response = client.get("/items", headers={"Authorization": "Bearer testtoken"})
assert response.status_code == 200, response.text

6
tests/test_security_oauth2_password_bearer_optional.py

@ -36,6 +36,12 @@ def test_incorrect_token():
assert response.json() == {"msg": "Create an account first"}
def test_empty_bearer_token():
response = client.get("/items", headers={"Authorization": "Bearer "})
assert response.status_code == 200, response.text
assert response.json() == {"msg": "Create an account first"}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text

Loading…
Cancel
Save