resolved RESOLVED PASS ยท 4 tool calls ยท 29 s ยท fastapi/fastapi
๐ Strip whitespaces from `Authorization` header credentials
## Description
Fixes whitespace handling in authorization header parameter extraction to comply with [RFC 6750](https://datatracker.ietf.org/doc/html/rfc6750#section-2.1).
## Problem
The current implementation preserves leading/trailing whitespace in the extracted credential parameter. While RFC 6750 allows one or more spaces (`1*SP`) between "Bearer" and the token, the token definition (`b64token`) doesn't include whitespace characters, meaning spaces should be treated as separators only.
This causes the extracted credentials to include extraneous whitespace, which can lead to authentication failures.
## Solution
Added `.strip()` to the parameter extraction to remove leading and trailing whitespace:
```python
return scheme, param.strip()
```
## Reproduction
### Before the fix:
```python
from typing import Annotated
from fastapi import Depends, FastAPI
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
app = FastAPI()
security = HTTPBearer()
@app.get("/users/me")
def read_current_user(
credentials: Annotated[HTTPAuthorizationCredentials, Depends(security)]
):
return {"scheme": credentials.scheme, "credentials": credentials.credentials}
```
**Test case 1: Single space (correct)**
```bash
curl -X 'GET' \
'http://127.0.0.1:8000/users/me' \
-H 'Authorization: Bearer xxx'
```
Response: `{"scheme":"Bearer","credentials":"xxx"}` โ
**Test case 2: Multiple spaces**
```bash
curl -X 'GET' \
'http://127.0.0.1:8000/users/me' \
-H 'Authorization: Bearer xxx'
```
Response: `{"scheme":"Bearer","credentials":" xxx"}` โ
**Test case 3: Trailing whitespace**
```bash
curl -X 'GET' \
'http://127.0.0.1:8000/users/me' \
-H 'Authorization: Bearer xxx '
```
Response: `{"scheme":"Bearer","credentials":"xxx "}` โ
### After the fix:
All three test cases now return: `{"scheme":"Bearer","credentials":"xxx"}` โ
## Changes
- Modified `get_authorization_scheme_param()` in `fastapi/security/utils.py` to strip whitespace from the parameter
- Added test cases to verify the fix handles multiple spaces and trailing whitespace correctly
## Checklist
- [x] Tests added to cover the bug fix
- [x] Existing tests pass| # | Tool | Arguments | Result |
|---|---|---|---|
| 1 | run_command | {
"command": "grep -rn \"get_authorization_scheme_param\" fastapi/security/utils.py"
} | {"status": "ok", "stdout": "fastapi/security/utils.py:4:def get_authorization_scheme_param(\n", "stderr": "", "exit_code": 0}full{"status": "ok", "stdout": "fastapi/security/utils.py:4:def get_authorization_scheme_param(\n", "stderr": "", "exit_code": 0} |
| 2 | run_command | {
"command": "sed -n '1,30p' fastapi/security/utils.py"
} | {"status": "ok", "stdout": "from typing import Optional\n\n\ndef get_authorization_scheme_param(\n authorization_header_value: Optional[str],\n) -> tuple[str, sfull{"status": "ok", "stdout": "from typing import Optional\n\n\ndef get_authorization_scheme_param(\n authorization_header_value: Optional[str],\n) -> tuple[str, str]:\n if not authorization_header_value:\n return \"\", \"\"\n scheme, _, param = authorization_header_value.partition(\" \")\n return scheme, param\n", "stderr": "", "exit_code": 0} |
| 3 | run_command | {
"command": "cd /workspace && python3 - <<'PY'\nfrom pathlib import Path\np = Path(\"fastapi/security/utils.py\"); s = p.read_text()\nold = \"\"\" scheme, _, param = authorization_header_value.partition(\" \")\n return scheme, param\"\"\"\nnew = \"\"\" scheme, _, param = authorization_header_value.partition(\" \")\n return scheme, param.strip()\"\"\"\nassert s.count(old) == 1, (\"old text found this many times:\", s.count(old))\nt = s.replace(old, new); compile(t, str(p), \"exec\"); p.write_text(t)\nPY\n"
} | {"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}full{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0} |
| 4 | run_command | {
"command": "cd /workspace && python3 - <<'PY'\nfrom fastapi.security.utils import get_authorization_scheme_param\n\ntest_cases = [\n (\"Bearer xxx\", (\"Bearer\", \"xxx\")),\n (\"Bearer xxx\", (\"Bearer\", \"xxx\")),\n (\"Bearer xxx \", (\"Bearer\", \"xxx\")),\n (\"Bearer xxx \", (\"Bearer\", \"xxx\")),\n (\"Basic yyy\", (\"Basic\", \"yyy\")),\n (\"Basic yyy \", (\"Basic\", \"yyy\")),\n (\"\", (\"\", \"\")),\n (None, (\"\", \"\")),\n]\n\nfor input_val, expected in test_cases:\n result = get_authorization_scheme_param(input_val)\n assert result == expected, f\"Failed for {input_val}: expected {expected}, got {result}\"\n\nprint(\"All tests passed!\")\nPY\n"
} | {"status": "ok", "stdout": "All tests passed!\n", "stderr": "", "exit_code": 0}full{"status": "ok", "stdout": "All tests passed!\n", "stderr": "", "exit_code": 0} |
| 5 | submit_patch | {} | {"status": "ok", "patch_size": 404, "files_changed": 1}full{"status": "ok", "patch_size": 404, "files_changed": 1} |
diff --git a/fastapi/security/utils.py b/fastapi/security/utils.py
index 002e68b4..fd349aec 100644
--- a/fastapi/security/utils.py
+++ b/fastapi/security/utils.py
@@ -7,4 +7,4 @@ def get_authorization_scheme_param(
if not authorization_header_value:
return "", ""
scheme, _, param = authorization_header_value.partition(" ")
- return scheme, param
+ return scheme, param.strip()
......... [100%]
=============================== warnings summary ===============================
../../../../../../../Users/jp/repos/kaggle-gemini-coding-agent-post-training/.envs/overlays/starlette-0.50.0-py3-none-any/starlette/testclient.py:45
/Users/jp/repos/kaggle-gemini-coding-agent-post-training/.envs/overlays/starlette-0.50.0-py3-none-any/starlette/testclient.py:45: DeprecationWarning: The anyio.abc.BlockingPortal alias is deprecated, use anyio.from_thread.BlockingPortal instead.
_PortalFactoryType = Callable[[], AbstractContextManager[anyio.abc.BlockingPortal]]
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
9 passed, 1 warning in 0.40s