Coverage for fastapi/security/http.py: 100%
90 statements
« prev ^ index » next coverage.py v7.6.1, created at 2025-05-05 00:03 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2025-05-05 00:03 +0000
1import binascii 1abcdef
2from base64 import b64decode 1abcdef
3from typing import Optional 1abcdef
5from fastapi.exceptions import HTTPException 1abcdef
6from fastapi.openapi.models import HTTPBase as HTTPBaseModel 1abcdef
7from fastapi.openapi.models import HTTPBearer as HTTPBearerModel 1abcdef
8from fastapi.security.base import SecurityBase 1abcdef
9from fastapi.security.utils import get_authorization_scheme_param 1abcdef
10from pydantic import BaseModel 1abcdef
11from starlette.requests import Request 1abcdef
12from starlette.status import HTTP_401_UNAUTHORIZED, HTTP_403_FORBIDDEN 1abcdef
13from typing_extensions import Annotated, Doc 1abcdef
16class HTTPBasicCredentials(BaseModel): 1abcdef
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.")] 1abcdef
26 password: Annotated[str, Doc("The HTTP Basic password.")] 1abcdef
29class HTTPAuthorizationCredentials(BaseModel): 1abcdef
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[ 1abcdef
52 str,
53 Doc(
54 """
55 The HTTP authorization scheme extracted from the header value.
56 """
57 ),
58 ]
59 credentials: Annotated[ 1abcdef
60 str,
61 Doc(
62 """
63 The HTTP authorization credentials extracted from the header value.
64 """
65 ),
66 ]
69class HTTPBase(SecurityBase): 1abcdef
70 def __init__( 1abcdef
71 self,
72 *,
73 scheme: str,
74 scheme_name: Optional[str] = None,
75 description: Optional[str] = None,
76 auto_error: bool = True,
77 ):
78 self.model = HTTPBaseModel(scheme=scheme, description=description) 1abcdef
79 self.scheme_name = scheme_name or self.__class__.__name__ 1abcdef
80 self.auto_error = auto_error 1abcdef
82 async def __call__( 1abcdef
83 self, request: Request
84 ) -> Optional[HTTPAuthorizationCredentials]:
85 authorization = request.headers.get("Authorization") 2YcXbZcYb0cZb1c0b2c1b3c2b4c3b5c4b6c5b7c6b8c7b9c8b!c9b#c!b$c#b%c$b'c%b(c'b
86 scheme, credentials = get_authorization_scheme_param(authorization) 2YcXbZcYb0cZb1c0b2c1b3c2b4c3b5c4b6c5b7c6b8c7b9c8b!c9b#c!b$c#b%c$b'c%b(c'b
87 if not (authorization and scheme and credentials): 2YcXbZcYb0cZb1c0b2c1b3c2b4c3b5c4b6c5b7c6b8c7b9c8b!c9b#c!b$c#b%c$b'c%b(c'b
88 if self.auto_error: 2XbYbZb0b1b2b3b4b5b6b7b8b9b!b#b$b%b'b
89 raise HTTPException( 2XbYb0b1b3b4b6b7b9b!b$b%b
90 status_code=HTTP_403_FORBIDDEN, detail="Not authenticated"
91 )
92 else:
93 return None 2Zb2b5b8b#b'b
94 return HTTPAuthorizationCredentials(scheme=scheme, credentials=credentials) 2YcZc0c1c2c3c4c5c6c7c8c9c!c#c$c%c'c(c
97class HTTPBasic(HTTPBase): 1abcdef
98 """
99 HTTP Basic authentication.
101 ## Usage
103 Create an instance object and use that object as the dependency in `Depends()`.
105 The dependency result will be an `HTTPBasicCredentials` object containing the
106 `username` and the `password`.
108 Read more about it in the
109 [FastAPI docs for HTTP Basic Auth](https://fastapi.tiangolo.com/advanced/security/http-basic-auth/).
111 ## Example
113 ```python
114 from typing import Annotated
116 from fastapi import Depends, FastAPI
117 from fastapi.security import HTTPBasic, HTTPBasicCredentials
119 app = FastAPI()
121 security = HTTPBasic()
124 @app.get("/users/me")
125 def read_current_user(credentials: Annotated[HTTPBasicCredentials, Depends(security)]):
126 return {"username": credentials.username, "password": credentials.password}
127 ```
128 """
130 def __init__( 1abcdef
131 self,
132 *,
133 scheme_name: Annotated[
134 Optional[str],
135 Doc(
136 """
137 Security scheme name.
139 It will be included in the generated OpenAPI (e.g. visible at `/docs`).
140 """
141 ),
142 ] = None,
143 realm: Annotated[
144 Optional[str],
145 Doc(
146 """
147 HTTP Basic authentication realm.
148 """
149 ),
150 ] = None,
151 description: Annotated[
152 Optional[str],
153 Doc(
154 """
155 Security scheme description.
157 It will be included in the generated OpenAPI (e.g. visible at `/docs`).
158 """
159 ),
160 ] = None,
161 auto_error: Annotated[
162 bool,
163 Doc(
164 """
165 By default, if the HTTP Basic authentication is not provided (a
166 header), `HTTPBasic` will automatically cancel the request and send the
167 client an error.
169 If `auto_error` is set to `False`, when the HTTP Basic authentication
170 is not available, instead of erroring out, the dependency result will
171 be `None`.
173 This is useful when you want to have optional authentication.
175 It is also useful when you want to have authentication that can be
176 provided in one of multiple optional ways (for example, in HTTP Basic
177 authentication or in an HTTP Bearer token).
178 """
179 ),
180 ] = True,
181 ):
182 self.model = HTTPBaseModel(scheme="basic", description=description) 1abcdef
183 self.scheme_name = scheme_name or self.__class__.__name__ 1abcdef
184 self.realm = realm 1abcdef
185 self.auto_error = auto_error 1abcdef
187 async def __call__( # type: ignore 1abcdef
188 self, request: Request
189 ) -> Optional[HTTPBasicCredentials]:
190 authorization = request.headers.get("Authorization") 2g 2 ? h i 3 @ j k 4 [ l m 5 ] n o 6 ^ p q 7 _ r s 8 ` t u 9 { v w ! | x y # } z A $ ~ B C % abD E ' bbF G ( cbH I ) dbJ K * ebL M + fbN O , gbP Q - hbR S . ibT U / jbV W : kbX Y ; lbZ 0 = mb1
191 scheme, param = get_authorization_scheme_param(authorization) 2g 2 ? h i 3 @ j k 4 [ l m 5 ] n o 6 ^ p q 7 _ r s 8 ` t u 9 { v w ! | x y # } z A $ ~ B C % abD E ' bbF G ( cbH I ) dbJ K * ebL M + fbN O , gbP Q - hbR S . ibT U / jbV W : kbX Y ; lbZ 0 = mb1
192 if self.realm: 2g 2 ? h i 3 @ j k 4 [ l m 5 ] n o 6 ^ p q 7 _ r s 8 ` t u 9 { v w ! | x y # } z A $ ~ B C % abD E ' bbF G ( cbH I ) dbJ K * ebL M + fbN O , gbP Q - hbR S . ibT U / jbV W : kbX Y ; lbZ 0 = mb1
193 unauthorized_headers = {"WWW-Authenticate": f'Basic realm="{self.realm}"'} 2i 3 @ j k 4 [ l q 7 _ r s 8 ` t y # } z A $ ~ B G ( cbH I ) dbJ O , gbP Q - hbR W : kbX Y ; lbZ
194 else:
195 unauthorized_headers = {"WWW-Authenticate": "Basic"} 2g 2 ? h m 5 ] n o 6 ^ p u 9 { v w ! | x C % abD E ' bbF K * ebL M + fbN S . ibT U / jbV 0 = mb1
196 if not authorization or scheme.lower() != "basic": 2g 2 ? h i 3 @ j k 4 [ l m 5 ] n o 6 ^ p q 7 _ r s 8 ` t u 9 { v w ! | x y # } z A $ ~ B C % abD E ' bbF G ( cbH I ) dbJ K * ebL M + fbN O , gbP Q - hbR S . ibT U / jbV W : kbX Y ; lbZ 0 = mb1
197 if self.auto_error: 2? @ [ ] ^ _ ` { | } ~ abbbcbdbebfbgbhbibjbkblbmb
198 raise HTTPException( 2@ [ ] _ ` { } ~ abcbdbebgbhbibkblbmb
199 status_code=HTTP_401_UNAUTHORIZED,
200 detail="Not authenticated",
201 headers=unauthorized_headers,
202 )
203 else:
204 return None 2? ^ | bbfbjb
205 invalid_user_credentials_exc = HTTPException( 1g2hi3jk4lm5no6pq7rs8tu9vw!xy#zA$BC%DE'FG(HI)JK*LM+NO,PQ-RS.TU/VW:XY;Z0=1
206 status_code=HTTP_401_UNAUTHORIZED,
207 detail="Invalid authentication credentials",
208 headers=unauthorized_headers,
209 )
210 try: 1g2hi3jk4lm5no6pq7rs8tu9vw!xy#zA$BC%DE'FG(HI)JK*LM+NO,PQ-RS.TU/VW:XY;Z0=1
211 data = b64decode(param).decode("ascii") 1g2hi3jk4lm5no6pq7rs8tu9vw!xy#zA$BC%DE'FG(HI)JK*LM+NO,PQ-RS.TU/VW:XY;Z0=1
212 except (ValueError, UnicodeDecodeError, binascii.Error): 123456789!#$%'()*+,-./:;=
213 raise invalid_user_credentials_exc # noqa: B904 123456789!#$%'()*+,-./:;=
214 username, separator, password = data.partition(":") 1ghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01
215 if not separator: 1ghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01
216 raise invalid_user_credentials_exc 1hjlnprtvxzBDFHJLNPRTVXZ1
217 return HTTPBasicCredentials(username=username, password=password) 1gikmoqsuwyACEGIKMOQSUWY0
220class HTTPBearer(HTTPBase): 1abcdef
221 """
222 HTTP Bearer token authentication.
224 ## Usage
226 Create an instance object and use that object as the dependency in `Depends()`.
228 The dependency result will be an `HTTPAuthorizationCredentials` object containing
229 the `scheme` and the `credentials`.
231 ## Example
233 ```python
234 from typing import Annotated
236 from fastapi import Depends, FastAPI
237 from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
239 app = FastAPI()
241 security = HTTPBearer()
244 @app.get("/users/me")
245 def read_current_user(
246 credentials: Annotated[HTTPAuthorizationCredentials, Depends(security)]
247 ):
248 return {"scheme": credentials.scheme, "credentials": credentials.credentials}
249 ```
250 """
252 def __init__( 1abcdef
253 self,
254 *,
255 bearerFormat: Annotated[Optional[str], Doc("Bearer token format.")] = None,
256 scheme_name: Annotated[
257 Optional[str],
258 Doc(
259 """
260 Security scheme name.
262 It will be included in the generated OpenAPI (e.g. visible at `/docs`).
263 """
264 ),
265 ] = None,
266 description: Annotated[
267 Optional[str],
268 Doc(
269 """
270 Security scheme description.
272 It will be included in the generated OpenAPI (e.g. visible at `/docs`).
273 """
274 ),
275 ] = None,
276 auto_error: Annotated[
277 bool,
278 Doc(
279 """
280 By default, if the HTTP Bearer token is not provided (in an
281 `Authorization` header), `HTTPBearer` will automatically cancel the
282 request and send the client an error.
284 If `auto_error` is set to `False`, when the HTTP Bearer token
285 is not available, instead of erroring out, the dependency result will
286 be `None`.
288 This is useful when you want to have optional authentication.
290 It is also useful when you want to have authentication that can be
291 provided in one of multiple optional ways (for example, in an HTTP
292 Bearer token or in a cookie).
293 """
294 ),
295 ] = True,
296 ):
297 self.model = HTTPBearerModel(bearerFormat=bearerFormat, description=description) 1abcdef
298 self.scheme_name = scheme_name or self.__class__.__name__ 1abcdef
299 self.auto_error = auto_error 1abcdef
301 async def __call__( 1abcdef
302 self, request: Request
303 ) -> Optional[HTTPAuthorizationCredentials]:
304 authorization = request.headers.get("Authorization") 2(bnb)b*bob+b,bpb-b.bqb/b:brb;b=bsb?b@btb[b]bub^b_bvb`b{bwb|b}bxb~bacybbccczbdcecAbfcgcBbhcicCbjckcDblcmcEbnc
305 scheme, credentials = get_authorization_scheme_param(authorization) 2(bnb)b*bob+b,bpb-b.bqb/b:brb;b=bsb?b@btb[b]bub^b_bvb`b{bwb|b}bxb~bacybbccczbdcecAbfcgcBbhcicCbjckcDblcmcEbnc
306 if not (authorization and scheme and credentials): 2(bnb)b*bob+b,bpb-b.bqb/b:brb;b=bsb?b@btb[b]bub^b_bvb`b{bwb|b}bxb~bacybbccczbdcecAbfcgcBbhcicCbjckcDblcmcEbnc
307 if self.auto_error: 2)b+b-b/b;b?b[b^b`b|b~bbcdcfchcjclcnc
308 raise HTTPException( 2)b+b/b;b[b^b|b~bdcfcjclc
309 status_code=HTTP_403_FORBIDDEN, detail="Not authenticated"
310 )
311 else:
312 return None 2-b?b`bbchcnc
313 if scheme.lower() != "bearer": 2(bnb*bob,bpb.bqb:brb=bsb@btb]bub_bvb{bwb}bxbacybcczbecAbgcBbicCbkcDbmcEb
314 if self.auto_error: 2nbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEb
315 raise HTTPException( 2nbobqbrbtbubwbxbzbAbCbDb
316 status_code=HTTP_403_FORBIDDEN,
317 detail="Invalid authentication credentials",
318 )
319 else:
320 return None 2pbsbvbybBbEb
321 return HTTPAuthorizationCredentials(scheme=scheme, credentials=credentials) 2(b*b,b.b:b=b@b]b_b{b}bacccecgcickcmc
324class HTTPDigest(HTTPBase): 1abcdef
325 """
326 HTTP Digest authentication.
328 ## Usage
330 Create an instance object and use that object as the dependency in `Depends()`.
332 The dependency result will be an `HTTPAuthorizationCredentials` object containing
333 the `scheme` and the `credentials`.
335 ## Example
337 ```python
338 from typing import Annotated
340 from fastapi import Depends, FastAPI
341 from fastapi.security import HTTPAuthorizationCredentials, HTTPDigest
343 app = FastAPI()
345 security = HTTPDigest()
348 @app.get("/users/me")
349 def read_current_user(
350 credentials: Annotated[HTTPAuthorizationCredentials, Depends(security)]
351 ):
352 return {"scheme": credentials.scheme, "credentials": credentials.credentials}
353 ```
354 """
356 def __init__( 1abcdef
357 self,
358 *,
359 scheme_name: Annotated[
360 Optional[str],
361 Doc(
362 """
363 Security scheme name.
365 It will be included in the generated OpenAPI (e.g. visible at `/docs`).
366 """
367 ),
368 ] = None,
369 description: Annotated[
370 Optional[str],
371 Doc(
372 """
373 Security scheme description.
375 It will be included in the generated OpenAPI (e.g. visible at `/docs`).
376 """
377 ),
378 ] = None,
379 auto_error: Annotated[
380 bool,
381 Doc(
382 """
383 By default, if the HTTP Digest is not provided, `HTTPDigest` will
384 automatically cancel the request and send the client an error.
386 If `auto_error` is set to `False`, when the HTTP Digest is not
387 available, instead of erroring out, the dependency result will
388 be `None`.
390 This is useful when you want to have optional authentication.
392 It is also useful when you want to have authentication that can be
393 provided in one of multiple optional ways (for example, in HTTP
394 Digest or in a cookie).
395 """
396 ),
397 ] = True,
398 ):
399 self.model = HTTPBaseModel(scheme="digest", description=description) 1abcdef
400 self.scheme_name = scheme_name or self.__class__.__name__ 1abcdef
401 self.auto_error = auto_error 1abcdef
403 async def __call__( 1abcdef
404 self, request: Request
405 ) -> Optional[HTTPAuthorizationCredentials]:
406 authorization = request.headers.get("Authorization") 2ocFbpcqcGbrcscHbtcucIbvcwcJbxcycKbzcAcLbBcCcMbDcEcNbFcGcObHcIcPbJcKcQbLcMcRbNcOcSbPcQcTbRcScUbTcUcVbVcWcWbXc
407 scheme, credentials = get_authorization_scheme_param(authorization) 2ocFbpcqcGbrcscHbtcucIbvcwcJbxcycKbzcAcLbBcCcMbDcEcNbFcGcObHcIcPbJcKcQbLcMcRbNcOcSbPcQcTbRcScUbTcUcVbVcWcWbXc
408 if not (authorization and scheme and credentials): 2ocFbpcqcGbrcscHbtcucIbvcwcJbxcycKbzcAcLbBcCcMbDcEcNbFcGcObHcIcPbJcKcQbLcMcRbNcOcSbPcQcTbRcScUbTcUcVbVcWcWbXc
409 if self.auto_error: 2pcrctcvcxczcBcDcFcHcJcLcNcPcRcTcVcXc
410 raise HTTPException( 2pcrcvcxcBcDcHcJcNcPcTcVc
411 status_code=HTTP_403_FORBIDDEN, detail="Not authenticated"
412 )
413 else:
414 return None 2tczcFcLcRcXc
415 if scheme.lower() != "digest": 2ocFbqcGbscHbucIbwcJbycKbAcLbCcMbEcNbGcObIcPbKcQbMcRbOcSbQcTbScUbUcVbWcWb
416 if self.auto_error: 2FbGbHbIbJbKbLbMbNbObPbQbRbSbTbUbVbWb
417 raise HTTPException( 2FbGbIbJbLbMbObPbRbSbUbVb
418 status_code=HTTP_403_FORBIDDEN,
419 detail="Invalid authentication credentials",
420 )
421 else:
422 return None 2HbKbNbQbTbWb
423 return HTTPAuthorizationCredentials(scheme=scheme, credentials=credentials) 2ocqcscucwcycAcCcEcGcIcKcMcOcQcScUcWc