Skip to content

Commit b421a94

Browse files
committed
Add RFC 6750 Bearer Token Auth
1 parent 4b23574 commit b421a94

3 files changed

Lines changed: 34 additions & 1 deletion

File tree

httpx/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ def main() -> None: # type: ignore
3838
"Auth",
3939
"BaseTransport",
4040
"BasicAuth",
41+
"BearerTokenAuth",
4142
"ByteStream",
4243
"Client",
4344
"CloseError",

httpx/_auth.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from hashlib import _Hash
1717

1818

19-
__all__ = ["Auth", "BasicAuth", "DigestAuth", "NetRCAuth"]
19+
__all__ = ["Auth", "BasicAuth", "BearerTokenAuth", "DigestAuth", "NetRCAuth"]
2020

2121

2222
class Auth:
@@ -142,6 +142,23 @@ def _build_auth_header(self, username: str | bytes, password: str | bytes) -> st
142142
return f"Basic {token}"
143143

144144

145+
class BearerTokenAuth(Auth):
146+
"""
147+
Allows the 'auth' argument to be passed as a bearer token string,
148+
and uses HTTP Bearer authentication (RFC 6750).
149+
"""
150+
151+
def __init__(self, bearer_token: str | bytes) -> None:
152+
self._auth_header = self._build_auth_header(bearer_token)
153+
154+
def auth_flow(self, request: Request) -> typing.Generator[Request, Response, None]:
155+
request.headers["Authorization"] = self._auth_header
156+
yield request
157+
158+
def _build_auth_header(self, bearer_token: str | bytes) -> str:
159+
return f"Bearer {to_bytes(bearer_token).decode()}"
160+
161+
145162
class NetRCAuth(Auth):
146163
"""
147164
Use a 'netrc' file to lookup basic auth credentials based on the url host.

tests/test_auth.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,21 @@ def test_basic_auth():
2626
flow.send(response)
2727

2828

29+
def test_bearer_token_auth():
30+
auth = httpx.BearerTokenAuth(bearer_token="my_token")
31+
request = httpx.Request("GET", "https://www.example.com")
32+
33+
# The initial request should include a bearer token auth header.
34+
flow = auth.sync_auth_flow(request)
35+
request = next(flow)
36+
assert request.headers["Authorization"].startswith("Bearer ")
37+
38+
# No other requests are made.
39+
response = httpx.Response(content=b"Hello, world!", status_code=200)
40+
with pytest.raises(StopIteration):
41+
flow.send(response)
42+
43+
2944
def test_digest_auth_with_200():
3045
auth = httpx.DigestAuth(username="user", password="pass")
3146
request = httpx.Request("GET", "https://www.example.com")

0 commit comments

Comments
 (0)