Coverage for fastapi / security / http.py: 100%
94 statements
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-12 18:15 +0000
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-12 18:15 +0000
1import binascii 1abcd
2from base64 import b64decode 1abcd
3from typing import Annotated 1abcd
5from annotated_doc import Doc 1abcd
6from fastapi.exceptions import HTTPException 1abcd
7from fastapi.openapi.models import HTTPBase as HTTPBaseModel 1abcd
8from fastapi.openapi.models import HTTPBearer as HTTPBearerModel 1abcd
9from fastapi.security.base import SecurityBase 1abcd
10from fastapi.security.utils import get_authorization_scheme_param 1abcd
11from pydantic import BaseModel 1abcd
12from starlette.requests import Request 1abcd
13from starlette.status import HTTP_401_UNAUTHORIZED 1abcd
16class HTTPBasicCredentials(BaseModel): 1abcd
17 """
18 The HTTP Basic credentials given as the result of using `HTTPBasic` in a
19 dependency.
21 Read more about it in the
22 [FastAPI docs for HTTP Basic Auth](https://fastapi.tiangolo.com/advanced/security/http-basic-auth/).
23 """
25 username: Annotated[str, Doc("The HTTP Basic username.")] 1abcd
26 password: Annotated[str, Doc("The HTTP Basic password.")] 1abcd
29class HTTPAuthorizationCredentials(BaseModel): 1abcd
30 """
31 The HTTP authorization credentials in the result of using `HTTPBearer` or
32 `HTTPDigest` in a dependency.
34 The HTTP authorization header value is split by the first space.
36 The first part is the `scheme`, the second part is the `credentials`.
38 For example, in an HTTP Bearer token scheme, the client will send a header
39 like:
41 ```
42 Authorization: Bearer deadbeef12346
43 ```
45 In this case:
47 * `scheme` will have the value `"Bearer"`
48 * `credentials` will have the value `"deadbeef12346"`
49 """
51 scheme: Annotated[ 1abcd
52 str,
53 Doc(
54 """
55 The HTTP authorization scheme extracted from the header value.
56 """
57 ),
58 ]
59 credentials: Annotated[ 1abcd
60 str,
61 Doc(
62 """
63 The HTTP authorization credentials extracted from the header value.
64 """
65 ),
66 ]
69class HTTPBase(SecurityBase): 1abcd
70 def __init__( 1abcd
71 self,
72 *,
73 scheme: str,
74 scheme_name: str | None = None,
75 description: str | None = None,
76 auto_error: bool = True,
77 ):
78 self.model: HTTPBaseModel = HTTPBaseModel( 1abcd
79 scheme=scheme, description=description
80 )
81 self.scheme_name = scheme_name or self.__class__.__name__ 1abcd
82 self.auto_error = auto_error 1abcd
84 def make_authenticate_headers(self) -> dict[str, str]: 1abcd
85 return {"WWW-Authenticate": f"{self.model.scheme.title()}"} 2@ [ I ] J ^ K _ L ` { | } M ~ N abO bbP cbdbebfbQ gbR hbS ibT jbkb
87 def make_not_authenticated_error(self) -> HTTPException: 1abcd
88 return HTTPException( 2@ [ t e u U f v V g I ] J ^ K _ L ` { w W h x X i | } y j z Y k A Z l M ~ N abO bbP cbdbB 0 m C 1 n ebfbD o E 2 p F 3 q Q gbR hbS ibT jbkbG 4 r H 5 s
89 status_code=HTTP_401_UNAUTHORIZED,
90 detail="Not authenticated",
91 headers=self.make_authenticate_headers(),
92 )
94 async def __call__(self, request: Request) -> HTTPAuthorizationCredentials | None: 1abcd
95 authorization = request.headers.get("Authorization") 24b@ 5b6b[ 7brb8b| 9b!b} #bsb$beb%b'bfb(btb
96 scheme, credentials = get_authorization_scheme_param(authorization) 24b@ 5b6b[ 7brb8b| 9b!b} #bsb$beb%b'bfb(btb
97 if not (authorization and scheme and credentials): 24b@ 5b6b[ 7brb8b| 9b!b} #bsb$beb%b'bfb(btb
98 if self.auto_error: 2@ [ rb| } sbebfbtb
99 raise self.make_not_authenticated_error() 2@ [ | } ebfb
100 else:
101 return None 2rbsbtb
102 return HTTPAuthorizationCredentials(scheme=scheme, credentials=credentials) 24b5b6b7b8b9b!b#b$b%b'b(b
105class HTTPBasic(HTTPBase): 1abcd
106 """
107 HTTP Basic authentication.
109 Ref: https://datatracker.ietf.org/doc/html/rfc7617
111 ## Usage
113 Create an instance object and use that object as the dependency in `Depends()`.
115 The dependency result will be an `HTTPBasicCredentials` object containing the
116 `username` and the `password`.
118 Read more about it in the
119 [FastAPI docs for HTTP Basic Auth](https://fastapi.tiangolo.com/advanced/security/http-basic-auth/).
121 ## Example
123 ```python
124 from typing import Annotated
126 from fastapi import Depends, FastAPI
127 from fastapi.security import HTTPBasic, HTTPBasicCredentials
129 app = FastAPI()
131 security = HTTPBasic()
134 @app.get("/users/me")
135 def read_current_user(credentials: Annotated[HTTPBasicCredentials, Depends(security)]):
136 return {"username": credentials.username, "password": credentials.password}
137 ```
138 """
140 def __init__( 1abcd
141 self,
142 *,
143 scheme_name: Annotated[
144 str | None,
145 Doc(
146 """
147 Security scheme name.
149 It will be included in the generated OpenAPI (e.g. visible at `/docs`).
150 """
151 ),
152 ] = None,
153 realm: Annotated[
154 str | None,
155 Doc(
156 """
157 HTTP Basic authentication realm.
158 """
159 ),
160 ] = None,
161 description: Annotated[
162 str | None,
163 Doc(
164 """
165 Security scheme description.
167 It will be included in the generated OpenAPI (e.g. visible at `/docs`).
168 """
169 ),
170 ] = None,
171 auto_error: Annotated[
172 bool,
173 Doc(
174 """
175 By default, if the HTTP Basic authentication is not provided (a
176 header), `HTTPBasic` will automatically cancel the request and send the
177 client an error.
179 If `auto_error` is set to `False`, when the HTTP Basic authentication
180 is not available, instead of erroring out, the dependency result will
181 be `None`.
183 This is useful when you want to have optional authentication.
185 It is also useful when you want to have authentication that can be
186 provided in one of multiple optional ways (for example, in HTTP Basic
187 authentication or in an HTTP Bearer token).
188 """
189 ),
190 ] = True,
191 ):
192 self.model = HTTPBaseModel(scheme="basic", description=description) 1abcd
193 self.scheme_name = scheme_name or self.__class__.__name__ 1abcd
194 self.realm = realm 1abcd
195 self.auto_error = auto_error 1abcd
197 def make_authenticate_headers(self) -> dict[str, str]: 1abcd
198 if self.realm: 1teuUfvVgwWhxXiyjzYkAZlB0mC1nDoE2pF3qG4rH5s
199 return {"WWW-Authenticate": f'Basic realm="{self.realm}"'} 1uUfvVgzYkAZlE2pF3q
200 return {"WWW-Authenticate": "Basic"} 1tewWhxXiyjB0mC1nDoG4rH5s
202 async def __call__( # type: ignore 1abcd
203 self, request: Request
204 ) -> HTTPBasicCredentials | None:
205 authorization = request.headers.get("Authorization") 26 t ube 7 u U f 8 v V g 9 w W h ! x # $ X i % y vbj ' z Y k ( A Z l ) B 0 m * C + , 1 n - D wbo . E 2 p / F 3 q : G 4 r ; H = ? 5 s
206 scheme, param = get_authorization_scheme_param(authorization) 26 t ube 7 u U f 8 v V g 9 w W h ! x # $ X i % y vbj ' z Y k ( A Z l ) B 0 m * C + , 1 n - D wbo . E 2 p / F 3 q : G 4 r ; H = ? 5 s
207 if not authorization or scheme.lower() != "basic": 26 t ube 7 u U f 8 v V g 9 w W h ! x # $ X i % y vbj ' z Y k ( A Z l ) B 0 m * C + , 1 n - D wbo . E 2 p / F 3 q : G 4 r ; H = ? 5 s
208 if self.auto_error: 2ubU V W X vbY Z 0 1 wb2 3 4 5
209 raise self.make_not_authenticated_error() 1UVWXYZ012345
210 else:
211 return None 2ubvbwb
212 try: 16te7uf8vg9wh!x#$i%yj'zk(Al)Bm*C+,n-Do.Ep/Fq:Gr;H=?s
213 data = b64decode(param).decode("ascii") 16te7uf8vg9wh!x#$i%yj'zk(Al)Bm*C+,n-Do.Ep/Fq:Gr;H=?s
214 except (ValueError, UnicodeDecodeError, binascii.Error) as e: 1tuvwxyzABCDEFGH
215 raise self.make_not_authenticated_error() from e 1tuvwxyzABCDEFGH
216 username, separator, password = data.partition(":") 16e7f8g9h!#$i%j'k(l)m*+,n-o.p/q:r;=?s
217 if not separator: 16e7f8g9h!#$i%j'k(l)m*+,n-o.p/q:r;=?s
218 raise self.make_not_authenticated_error() 1efghijklmnopqrs
219 return HTTPBasicCredentials(username=username, password=password) 16789!#$%'()*+,-./:;=?
222class HTTPBearer(HTTPBase): 1abcd
223 """
224 HTTP Bearer token authentication.
226 ## Usage
228 Create an instance object and use that object as the dependency in `Depends()`.
230 The dependency result will be an `HTTPAuthorizationCredentials` object containing
231 the `scheme` and the `credentials`.
233 ## Example
235 ```python
236 from typing import Annotated
238 from fastapi import Depends, FastAPI
239 from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
241 app = FastAPI()
243 security = HTTPBearer()
246 @app.get("/users/me")
247 def read_current_user(
248 credentials: Annotated[HTTPAuthorizationCredentials, Depends(security)]
249 ):
250 return {"scheme": credentials.scheme, "credentials": credentials.credentials}
251 ```
252 """
254 def __init__( 1abcd
255 self,
256 *,
257 bearerFormat: Annotated[str | None, Doc("Bearer token format.")] = None,
258 scheme_name: Annotated[
259 str | None,
260 Doc(
261 """
262 Security scheme name.
264 It will be included in the generated OpenAPI (e.g. visible at `/docs`).
265 """
266 ),
267 ] = None,
268 description: Annotated[
269 str | None,
270 Doc(
271 """
272 Security scheme description.
274 It will be included in the generated OpenAPI (e.g. visible at `/docs`).
275 """
276 ),
277 ] = None,
278 auto_error: Annotated[
279 bool,
280 Doc(
281 """
282 By default, if the HTTP Bearer token is not provided (in an
283 `Authorization` header), `HTTPBearer` will automatically cancel the
284 request and send the client an error.
286 If `auto_error` is set to `False`, when the HTTP Bearer token
287 is not available, instead of erroring out, the dependency result will
288 be `None`.
290 This is useful when you want to have optional authentication.
292 It is also useful when you want to have authentication that can be
293 provided in one of multiple optional ways (for example, in an HTTP
294 Bearer token or in a cookie).
295 """
296 ),
297 ] = True,
298 ):
299 self.model = HTTPBearerModel(bearerFormat=bearerFormat, description=description) 1abcd
300 self.scheme_name = scheme_name or self.__class__.__name__ 1abcd
301 self.auto_error = auto_error 1abcd
303 async def __call__(self, request: Request) -> HTTPAuthorizationCredentials | None: 1abcd
304 authorization = request.headers.get("Authorization") 2xbI ] ybJ ^ zblbAbBb{ CbDbEbM ~ FbN abGbmbHbIbdbJbKbLbQ gbMbR hbNbnbObPbkbQbRb
305 scheme, credentials = get_authorization_scheme_param(authorization) 2xbI ] ybJ ^ zblbAbBb{ CbDbEbM ~ FbN abGbmbHbIbdbJbKbLbQ gbMbR hbNbnbObPbkbQbRb
306 if not (authorization and scheme and credentials): 2xbI ] ybJ ^ zblbAbBb{ CbDbEbM ~ FbN abGbmbHbIbdbJbKbLbQ gbMbR hbNbnbObPbkbQbRb
307 if self.auto_error: 2] ^ Ab{ Db~ abHbdbKbgbhbObkbRb
308 raise self.make_not_authenticated_error() 2] ^ { Db~ abdbKbgbhbkbRb
309 else:
310 return None 2AbHbOb
311 if scheme.lower() != "bearer": 2xbI ybJ zblbBbCbEbM FbN GbmbIbJbLbQ MbR NbnbPbQb
312 if self.auto_error: 2I J lbM N mbQ R nb
313 raise self.make_not_authenticated_error() 1IJMNQR
314 else:
315 return None 2lbmbnb
316 return HTTPAuthorizationCredentials(scheme=scheme, credentials=credentials) 2xbybzbBbCbEbFbGbIbJbLbMbNbPbQb
319class HTTPDigest(HTTPBase): 1abcd
320 """
321 HTTP Digest authentication.
323 **Warning**: this is only a stub to connect the components with OpenAPI in FastAPI,
324 but it doesn't implement the full Digest scheme, you would need to to subclass it
325 and implement it in your code.
327 Ref: https://datatracker.ietf.org/doc/html/rfc7616
329 ## Usage
331 Create an instance object and use that object as the dependency in `Depends()`.
333 The dependency result will be an `HTTPAuthorizationCredentials` object containing
334 the `scheme` and the `credentials`.
336 ## Example
338 ```python
339 from typing import Annotated
341 from fastapi import Depends, FastAPI
342 from fastapi.security import HTTPAuthorizationCredentials, HTTPDigest
344 app = FastAPI()
346 security = HTTPDigest()
349 @app.get("/users/me")
350 def read_current_user(
351 credentials: Annotated[HTTPAuthorizationCredentials, Depends(security)]
352 ):
353 return {"scheme": credentials.scheme, "credentials": credentials.credentials}
354 ```
355 """
357 def __init__( 1abcd
358 self,
359 *,
360 scheme_name: Annotated[
361 str | None,
362 Doc(
363 """
364 Security scheme name.
366 It will be included in the generated OpenAPI (e.g. visible at `/docs`).
367 """
368 ),
369 ] = None,
370 description: Annotated[
371 str | None,
372 Doc(
373 """
374 Security scheme description.
376 It will be included in the generated OpenAPI (e.g. visible at `/docs`).
377 """
378 ),
379 ] = None,
380 auto_error: Annotated[
381 bool,
382 Doc(
383 """
384 By default, if the HTTP Digest is not provided, `HTTPDigest` will
385 automatically cancel the request and send the client an error.
387 If `auto_error` is set to `False`, when the HTTP Digest is not
388 available, instead of erroring out, the dependency result will
389 be `None`.
391 This is useful when you want to have optional authentication.
393 It is also useful when you want to have authentication that can be
394 provided in one of multiple optional ways (for example, in HTTP
395 Digest or in a cookie).
396 """
397 ),
398 ] = True,
399 ):
400 self.model = HTTPBaseModel(scheme="digest", description=description) 1abcd
401 self.scheme_name = scheme_name or self.__class__.__name__ 1abcd
402 self.auto_error = auto_error 1abcd
404 async def __call__(self, request: Request) -> HTTPAuthorizationCredentials | None: 1abcd
405 authorization = request.headers.get("Authorization") 2SbK _ TbL ` UbobVbWbO bbXbP cbYbpbZb0bS ib1bT jb2bqb3b
406 scheme, credentials = get_authorization_scheme_param(authorization) 2SbK _ TbL ` UbobVbWbO bbXbP cbYbpbZb0bS ib1bT jb2bqb3b
407 if not (authorization and scheme and credentials): 2SbK _ TbL ` UbobVbWbO bbXbP cbYbpbZb0bS ib1bT jb2bqb3b
408 if self.auto_error: 2_ ` VbbbcbZbibjb3b
409 raise self.make_not_authenticated_error() 2_ ` bbcbibjb
410 else:
411 return None 2VbZb3b
412 if scheme.lower() != "digest": 2SbK TbL UbobWbO XbP Ybpb0bS 1bT 2bqb
413 if self.auto_error: 2K L obO P pbS T qb
414 raise self.make_not_authenticated_error() 1KLOPST
415 else:
416 return None 2obpbqb
417 return HTTPAuthorizationCredentials(scheme=scheme, credentials=credentials) 2SbTbUbWbXbYb0b1b2b