Coverage for fastapi/security/http.py: 100%
95 statements
« prev ^ index » next coverage.py v7.6.1, created at 2025-12-04 08:29 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2025-12-04 08:29 +0000
1import binascii 1abcdefg
2from base64 import b64decode 1abcdefg
3from typing import Dict, Optional 1abcdefg
5from annotated_doc import Doc 1abcdefg
6from fastapi.exceptions import HTTPException 1abcdefg
7from fastapi.openapi.models import HTTPBase as HTTPBaseModel 1abcdefg
8from fastapi.openapi.models import HTTPBearer as HTTPBearerModel 1abcdefg
9from fastapi.security.base import SecurityBase 1abcdefg
10from fastapi.security.utils import get_authorization_scheme_param 1abcdefg
11from pydantic import BaseModel 1abcdefg
12from starlette.requests import Request 1abcdefg
13from starlette.status import HTTP_401_UNAUTHORIZED 1abcdefg
14from typing_extensions import Annotated 1abcdefg
17class HTTPBasicCredentials(BaseModel): 1abcdefg
18 """
19 The HTTP Basic credentials given as the result of using `HTTPBasic` in a
20 dependency.
22 Read more about it in the
23 [FastAPI docs for HTTP Basic Auth](https://fastapi.tiangolo.com/advanced/security/http-basic-auth/).
24 """
26 username: Annotated[str, Doc("The HTTP Basic username.")] 1abcdefg
27 password: Annotated[str, Doc("The HTTP Basic password.")] 1abcdefg
30class HTTPAuthorizationCredentials(BaseModel): 1abcdefg
31 """
32 The HTTP authorization credentials in the result of using `HTTPBearer` or
33 `HTTPDigest` in a dependency.
35 The HTTP authorization header value is split by the first space.
37 The first part is the `scheme`, the second part is the `credentials`.
39 For example, in an HTTP Bearer token scheme, the client will send a header
40 like:
42 ```
43 Authorization: Bearer deadbeef12346
44 ```
46 In this case:
48 * `scheme` will have the value `"Bearer"`
49 * `credentials` will have the value `"deadbeef12346"`
50 """
52 scheme: Annotated[ 1abcdefg
53 str,
54 Doc(
55 """
56 The HTTP authorization scheme extracted from the header value.
57 """
58 ),
59 ]
60 credentials: Annotated[ 1abcdefg
61 str,
62 Doc(
63 """
64 The HTTP authorization credentials extracted from the header value.
65 """
66 ),
67 ]
70class HTTPBase(SecurityBase): 1abcdefg
71 def __init__( 1abcdefg
72 self,
73 *,
74 scheme: str,
75 scheme_name: Optional[str] = None,
76 description: Optional[str] = None,
77 auto_error: bool = True,
78 ):
79 self.model: HTTPBaseModel = HTTPBaseModel( 1abcdefg
80 scheme=scheme, description=description
81 )
82 self.scheme_name = scheme_name or self.__class__.__name__ 1abcdefg
83 self.auto_error = auto_error 1abcdefg
85 def make_authenticate_headers(self) -> Dict[str, str]: 1abcdefg
86 return {"WWW-Authenticate": f"{self.model.scheme.title()}"} 2Zb0b# 1b$ 2b% 3b' 4b5b6b7b( 8b) 9b* !b+ #b$b%b'b, (b- )b. *b/ +b,b-b.b: /b; :b= ;b? =b?b@b[b@ ]b[ ^b] _b^ `b{b|b}b_ ~b` ac{ bc| ccdcecfc} gc~ hcabicbbjckc
88 def make_not_authenticated_error(self) -> HTTPException: 1abcdefg
89 return HTTPException( 2Zb0bJ h K cbi L dbj # 1b$ 2b% 3b' 4b5bM ebk 6b7bN l O fbm P gbn ( 8b) 9b* !b+ #b$bQ hbo %b'bR p S ibq T jbr , (b- )b. *b/ +b,bU kbs -b.bV t W lbu X mbv : /b; :b= ;b? =b?bY nbw @b[bZ x 0 oby 1 pbz @ ]b[ ^b] _b^ `b{b2 qbA |b}b3 B 4 rbC 5 sbD _ ~b` ac{ bc| ccdc6 tbE ecfc7 F 8 ubG 9 vbH } gc~ hcabicbbjckc! wbI
90 status_code=HTTP_401_UNAUTHORIZED,
91 detail="Not authenticated",
92 headers=self.make_authenticate_headers(),
93 )
95 async def __call__( 1abcdefg
96 self, request: Request
97 ) -> Optional[HTTPAuthorizationCredentials]:
98 authorization = request.headers.get("Authorization") 2BdZbCd0bDdzcEd6bFd7bGdAcHd%bId'bJdBcKd-bLd.bMdCcNd@bOd[bPdDcQd|bRd}bSdEcTdecUdfcVdFc
99 scheme, credentials = get_authorization_scheme_param(authorization) 2BdZbCd0bDdzcEd6bFd7bGdAcHd%bId'bJdBcKd-bLd.bMdCcNd@bOd[bPdDcQd|bRd}bSdEcTdecUdfcVdFc
100 if not (authorization and scheme and credentials): 2BdZbCd0bDdzcEd6bFd7bGdAcHd%bId'bJdBcKd-bLd.bMdCcNd@bOd[bPdDcQd|bRd}bSdEcTdecUdfcVdFc
101 if self.auto_error: 2Zb0bzc6b7bAc%b'bBc-b.bCc@b[bDc|b}bEcecfcFc
102 raise self.make_not_authenticated_error() 2Zb0b6b7b%b'b-b.b@b[b|b}becfc
103 else:
104 return None 2zcAcBcCcDcEcFc
105 return HTTPAuthorizationCredentials(scheme=scheme, credentials=credentials) 2BdCdDdEdFdGdHdIdJdKdLdMdNdOdPdQdRdSdTdUdVd
108class HTTPBasic(HTTPBase): 1abcdefg
109 """
110 HTTP Basic authentication.
112 Ref: https://datatracker.ietf.org/doc/html/rfc7617
114 ## Usage
116 Create an instance object and use that object as the dependency in `Depends()`.
118 The dependency result will be an `HTTPBasicCredentials` object containing the
119 `username` and the `password`.
121 Read more about it in the
122 [FastAPI docs for HTTP Basic Auth](https://fastapi.tiangolo.com/advanced/security/http-basic-auth/).
124 ## Example
126 ```python
127 from typing import Annotated
129 from fastapi import Depends, FastAPI
130 from fastapi.security import HTTPBasic, HTTPBasicCredentials
132 app = FastAPI()
134 security = HTTPBasic()
137 @app.get("/users/me")
138 def read_current_user(credentials: Annotated[HTTPBasicCredentials, Depends(security)]):
139 return {"username": credentials.username, "password": credentials.password}
140 ```
141 """
143 def __init__( 1abcdefg
144 self,
145 *,
146 scheme_name: Annotated[
147 Optional[str],
148 Doc(
149 """
150 Security scheme name.
152 It will be included in the generated OpenAPI (e.g. visible at `/docs`).
153 """
154 ),
155 ] = None,
156 realm: Annotated[
157 Optional[str],
158 Doc(
159 """
160 HTTP Basic authentication realm.
161 """
162 ),
163 ] = None,
164 description: Annotated[
165 Optional[str],
166 Doc(
167 """
168 Security scheme description.
170 It will be included in the generated OpenAPI (e.g. visible at `/docs`).
171 """
172 ),
173 ] = None,
174 auto_error: Annotated[
175 bool,
176 Doc(
177 """
178 By default, if the HTTP Basic authentication is not provided (a
179 header), `HTTPBasic` will automatically cancel the request and send the
180 client an error.
182 If `auto_error` is set to `False`, when the HTTP Basic authentication
183 is not available, instead of erroring out, the dependency result will
184 be `None`.
186 This is useful when you want to have optional authentication.
188 It is also useful when you want to have authentication that can be
189 provided in one of multiple optional ways (for example, in HTTP Basic
190 authentication or in an HTTP Bearer token).
191 """
192 ),
193 ] = True,
194 ):
195 self.model = HTTPBaseModel(scheme="basic", description=description) 1abcdefg
196 self.scheme_name = scheme_name or self.__class__.__name__ 1abcdefg
197 self.realm = realm 1abcdefg
198 self.auto_error = auto_error 1abcdefg
200 def make_authenticate_headers(self) -> Dict[str, str]: 1abcdefg
201 if self.realm: 2J h K cbi L dbj M ebk N l O fbm P gbn Q hbo R p S ibq T jbr U kbs V t W lbu X mbv Y nbw Z x 0 oby 1 pbz 2 qbA 3 B 4 rbC 5 sbD 6 tbE 7 F 8 ubG 9 vbH ! wbI
202 return {"WWW-Authenticate": f'Basic realm="{self.realm}"'} 2K cbi L dbj O fbm P gbn S ibq T jbr W lbu X mbv 0 oby 1 pbz 4 rbC 5 sbD 8 ubG 9 vbH
203 return {"WWW-Authenticate": "Basic"} 2J h M ebk N l Q hbo R p U kbs V t Y nbw Z x 2 qbA 3 B 6 tbE 7 F ! wbI
205 async def __call__( # type: ignore 1abcdefg
206 self, request: Request
207 ) -> Optional[HTTPBasicCredentials]:
208 authorization = request.headers.get("Authorization") 2xbJ Gch ybK cbi zbL dbj AbM ebk BbN Hcl CbO fbm DbP gbn EbQ hbo FbR Icp GbS ibq HbT jbr IbU kbs JbV Jct KbW lbu LbX mbv MbY nbw NbZ Kcx Ob0 oby Pb1 pbz Qb2 qbA Rb3 LcB Sb4 rbC Tb5 sbD Ub6 tbE Vb7 McF Wb8 ubG Xb9 vbH Yb! wbI
209 scheme, param = get_authorization_scheme_param(authorization) 2xbJ Gch ybK cbi zbL dbj AbM ebk BbN Hcl CbO fbm DbP gbn EbQ hbo FbR Icp GbS ibq HbT jbr IbU kbs JbV Jct KbW lbu LbX mbv MbY nbw NbZ Kcx Ob0 oby Pb1 pbz Qb2 qbA Rb3 LcB Sb4 rbC Tb5 sbD Ub6 tbE Vb7 McF Wb8 ubG Xb9 vbH Yb! wbI
210 if not authorization or scheme.lower() != "basic": 2xbJ Gch ybK cbi zbL dbj AbM ebk BbN Hcl CbO fbm DbP gbn EbQ hbo FbR Icp GbS ibq HbT jbr IbU kbs JbV Jct KbW lbu LbX mbv MbY nbw NbZ Kcx Ob0 oby Pb1 pbz Qb2 qbA Rb3 LcB Sb4 rbC Tb5 sbD Ub6 tbE Vb7 McF Wb8 ubG Xb9 vbH Yb! wbI
211 if self.auto_error: 2GccbdbebHcfbgbhbIcibjbkbJclbmbnbKcobpbqbLcrbsbtbMcubvbwb
212 raise self.make_not_authenticated_error() 2cbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwb
213 else:
214 return None 2GcHcIcJcKcLcMc
215 try: 2xbJ h ybK i zbL j AbM k BbN l CbO m DbP n EbQ o FbR p GbS q HbT r IbU s JbV t KbW u LbX v MbY w NbZ x Ob0 y Pb1 z Qb2 A Rb3 B Sb4 C Tb5 D Ub6 E Vb7 F Wb8 G Xb9 H Yb! I
216 data = b64decode(param).decode("ascii") 2xbJ h ybK i zbL j AbM k BbN l CbO m DbP n EbQ o FbR p GbS q HbT r IbU s JbV t KbW u LbX v MbY w NbZ x Ob0 y Pb1 z Qb2 A Rb3 B Sb4 C Tb5 D Ub6 E Vb7 F Wb8 G Xb9 H Yb! I
217 except (ValueError, UnicodeDecodeError, binascii.Error) as e: 1JKLMNOPQRSTUVWXYZ0123456789!
218 raise self.make_not_authenticated_error() from e 1JKLMNOPQRSTUVWXYZ0123456789!
219 username, separator, password = data.partition(":") 2xbh ybi zbj Abk Bbl Cbm Dbn Ebo Fbp Gbq Hbr Ibs Jbt Kbu Lbv Mbw Nbx Oby Pbz QbA RbB SbC TbD UbE VbF WbG XbH YbI
220 if not separator: 2xbh ybi zbj Abk Bbl Cbm Dbn Ebo Fbp Gbq Hbr Ibs Jbt Kbu Lbv Mbw Nbx Oby Pbz QbA RbB SbC TbD UbE VbF WbG XbH YbI
221 raise self.make_not_authenticated_error() 1hijklmnopqrstuvwxyzABCDEFGHI
222 return HTTPBasicCredentials(username=username, password=password) 2xbybzbAbBbCbDbEbFbGbHbIbJbKbLbMbNbObPbQbRbSbTbUbVbWbXbYb
225class HTTPBearer(HTTPBase): 1abcdefg
226 """
227 HTTP Bearer token authentication.
229 ## Usage
231 Create an instance object and use that object as the dependency in `Depends()`.
233 The dependency result will be an `HTTPAuthorizationCredentials` object containing
234 the `scheme` and the `credentials`.
236 ## Example
238 ```python
239 from typing import Annotated
241 from fastapi import Depends, FastAPI
242 from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
244 app = FastAPI()
246 security = HTTPBearer()
249 @app.get("/users/me")
250 def read_current_user(
251 credentials: Annotated[HTTPAuthorizationCredentials, Depends(security)]
252 ):
253 return {"scheme": credentials.scheme, "credentials": credentials.credentials}
254 ```
255 """
257 def __init__( 1abcdefg
258 self,
259 *,
260 bearerFormat: Annotated[Optional[str], Doc("Bearer token format.")] = None,
261 scheme_name: Annotated[
262 Optional[str],
263 Doc(
264 """
265 Security scheme name.
267 It will be included in the generated OpenAPI (e.g. visible at `/docs`).
268 """
269 ),
270 ] = None,
271 description: Annotated[
272 Optional[str],
273 Doc(
274 """
275 Security scheme description.
277 It will be included in the generated OpenAPI (e.g. visible at `/docs`).
278 """
279 ),
280 ] = None,
281 auto_error: Annotated[
282 bool,
283 Doc(
284 """
285 By default, if the HTTP Bearer token is not provided (in an
286 `Authorization` header), `HTTPBearer` will automatically cancel the
287 request and send the client an error.
289 If `auto_error` is set to `False`, when the HTTP Bearer token
290 is not available, instead of erroring out, the dependency result will
291 be `None`.
293 This is useful when you want to have optional authentication.
295 It is also useful when you want to have authentication that can be
296 provided in one of multiple optional ways (for example, in an HTTP
297 Bearer token or in a cookie).
298 """
299 ),
300 ] = True,
301 ):
302 self.model = HTTPBearerModel(bearerFormat=bearerFormat, description=description) 1abcdefg
303 self.scheme_name = scheme_name or self.__class__.__name__ 1abcdefg
304 self.auto_error = auto_error 1abcdefg
306 async def __call__( 1abcdefg
307 self, request: Request
308 ) -> Optional[HTTPAuthorizationCredentials]:
309 authorization = request.headers.get("Authorization") 2Nc# 1bOc$ 2bPclcQcRc5bScTcUc( 8bVc) 9bWcmcXcYc$bZc0c1c, (b2c- )b3cnc4c5c,b6c7c8c: /b9c; :b!coc#c$c?b%c'c(c@ ]b)c[ ^b*cpc+c,c{b-c.c/c_ ~b:c` ac;cqc=c?cdc@c[c]c} gc^c~ hc_crc`c{ckc|c}c
310 scheme, credentials = get_authorization_scheme_param(authorization) 2Nc# 1bOc$ 2bPclcQcRc5bScTcUc( 8bVc) 9bWcmcXcYc$bZc0c1c, (b2c- )b3cnc4c5c,b6c7c8c: /b9c; :b!coc#c$c?b%c'c(c@ ]b)c[ ^b*cpc+c,c{b-c.c/c_ ~b:c` ac;cqc=c?cdc@c[c]c} gc^c~ hc_crc`c{ckc|c}c
311 if not (authorization and scheme and credentials): 2Nc# 1bOc$ 2bPclcQcRc5bScTcUc( 8bVc) 9bWcmcXcYc$bZc0c1c, (b2c- )b3cnc4c5c,b6c7c8c: /b9c; :b!coc#c$c?b%c'c(c@ ]b)c[ ^b*cpc+c,c{b-c.c/c_ ~b:c` ac;cqc=c?cdc@c[c]c} gc^c~ hc_crc`c{ckc|c}c
312 if self.auto_error: 21b2bQc5bTc8b9bXc$b0c(b)b4c,b7c/b:b#c?b'c]b^b+c{b.c~bac=cdc[cgchc`ckc}c
313 raise self.make_not_authenticated_error() 21b2b5bTc8b9b$b0c(b)b,b7c/b:b?b'c]b^b{b.c~bacdc[cgchckc}c
314 else:
315 return None 2QcXc4c#c+c=c`c
316 if scheme.lower() != "bearer": 2Nc# Oc$ PclcRcScUc( Vc) WcmcYcZc1c, 2c- 3cnc5c6c8c: 9c; !coc$c%c(c@ )c[ *cpc,c-c/c_ :c` ;cqc?c@c]c} ^c~ _crc{c|c
317 if self.auto_error: 2# $ lc( ) mc, - nc: ; oc@ [ pc_ ` qc} ~ rc
318 raise self.make_not_authenticated_error() 1#$(),-:;@[_`}~
319 else:
320 return None 2lcmcncocpcqcrc
321 return HTTPAuthorizationCredentials(scheme=scheme, credentials=credentials) 2NcOcPcRcScUcVcWcYcZc1c2c3c5c6c8c9c!c$c%c(c)c*c,c-c/c:c;c?c@c]c^c_c{c|c
324class HTTPDigest(HTTPBase): 1abcdefg
325 """
326 HTTP Digest authentication.
328 **Warning**: this is only a stub to connect the components with OpenAPI in FastAPI,
329 but it doesn't implement the full Digest scheme, you would need to to subclass it
330 and implement it in your code.
332 Ref: https://datatracker.ietf.org/doc/html/rfc7616
334 ## Usage
336 Create an instance object and use that object as the dependency in `Depends()`.
338 The dependency result will be an `HTTPAuthorizationCredentials` object containing
339 the `scheme` and the `credentials`.
341 ## Example
343 ```python
344 from typing import Annotated
346 from fastapi import Depends, FastAPI
347 from fastapi.security import HTTPAuthorizationCredentials, HTTPDigest
349 app = FastAPI()
351 security = HTTPDigest()
354 @app.get("/users/me")
355 def read_current_user(
356 credentials: Annotated[HTTPAuthorizationCredentials, Depends(security)]
357 ):
358 return {"scheme": credentials.scheme, "credentials": credentials.credentials}
359 ```
360 """
362 def __init__( 1abcdefg
363 self,
364 *,
365 scheme_name: Annotated[
366 Optional[str],
367 Doc(
368 """
369 Security scheme name.
371 It will be included in the generated OpenAPI (e.g. visible at `/docs`).
372 """
373 ),
374 ] = None,
375 description: Annotated[
376 Optional[str],
377 Doc(
378 """
379 Security scheme description.
381 It will be included in the generated OpenAPI (e.g. visible at `/docs`).
382 """
383 ),
384 ] = None,
385 auto_error: Annotated[
386 bool,
387 Doc(
388 """
389 By default, if the HTTP Digest is not provided, `HTTPDigest` will
390 automatically cancel the request and send the client an error.
392 If `auto_error` is set to `False`, when the HTTP Digest is not
393 available, instead of erroring out, the dependency result will
394 be `None`.
396 This is useful when you want to have optional authentication.
398 It is also useful when you want to have authentication that can be
399 provided in one of multiple optional ways (for example, in HTTP
400 Digest or in a cookie).
401 """
402 ),
403 ] = True,
404 ):
405 self.model = HTTPBaseModel(scheme="digest", description=description) 1abcdefg
406 self.scheme_name = scheme_name or self.__class__.__name__ 1abcdefg
407 self.auto_error = auto_error 1abcdefg
409 async def __call__( 1abcdefg
410 self, request: Request
411 ) -> Optional[HTTPAuthorizationCredentials]:
412 authorization = request.headers.get("Authorization") 2~c% 3bad' 4bbdsccddd* !bed+ #bfdtcgdhd. *bid/ +bjduckdld= ;bmd? =bndvcodpd] _bqd^ `brdwcsdtd{ bcud| ccvdxcwdxdabicydbbjczdycAd
413 scheme, credentials = get_authorization_scheme_param(authorization) 2~c% 3bad' 4bbdsccddd* !bed+ #bfdtcgdhd. *bid/ +bjduckdld= ;bmd? =bndvcodpd] _bqd^ `brdwcsdtd{ bcud| ccvdxcwdxdabicydbbjczdycAd
414 if not (authorization and scheme and credentials): 2~c% 3bad' 4bbdsccddd* !bed+ #bfdtcgdhd. *bid/ +bjduckdld= ;bmd? =bndvcodpd] _bqd^ `brdwcsdtd{ bcud| ccvdxcwdxdabicydbbjczdycAd
415 if self.auto_error: 23b4bcd!b#bgd*b+bkd;b=bod_b`bsdbcccwdicjcAd
416 raise self.make_not_authenticated_error() 23b4b!b#b*b+b;b=b_b`bbcccicjc
417 else:
418 return None 2cdgdkdodsdwdAd
419 if scheme.lower() != "digest": 2~c% ad' bdscdd* ed+ fdtchd. id/ jducld= md? ndvcpd] qd^ rdwctd{ ud| vdxcxdabydbbzdyc
420 if self.auto_error: 2% ' sc* + tc. / uc= ? vc] ^ wc{ | xcabbbyc
421 raise self.make_not_authenticated_error() 2% ' * + . / = ? ] ^ { | abbb
422 else:
423 return None 2sctcucvcwcxcyc
424 return HTTPAuthorizationCredentials(scheme=scheme, credentials=credentials) 2~cadbdddedfdhdidjdldmdndpdqdrdtdudvdxdydzd