Coverage for fastapi / security / http.py: 100%
94 statements
« prev ^ index » next coverage.py v7.13.3, created at 2026-04-06 01:24 +0000
« prev ^ index » next coverage.py v7.13.3, created at 2026-04-06 01:24 +0000
1import binascii 1adbc
2from base64 import b64decode 1adbc
3from typing import Annotated 1adbc
5from annotated_doc import Doc 1adbc
6from fastapi.exceptions import HTTPException 1adbc
7from fastapi.openapi.models import HTTPBase as HTTPBaseModel 1adbc
8from fastapi.openapi.models import HTTPBearer as HTTPBearerModel 1adbc
9from fastapi.security.base import SecurityBase 1adbc
10from fastapi.security.utils import get_authorization_scheme_param 1adbc
11from pydantic import BaseModel 1adbc
12from starlette.requests import Request 1adbc
13from starlette.status import HTTP_401_UNAUTHORIZED 1adbc
16class HTTPBasicCredentials(BaseModel): 1adbc
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.")] 1abc
26 password: Annotated[str, Doc("The HTTP Basic password.")] 1abc
29class HTTPAuthorizationCredentials(BaseModel): 1adbc
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[ 1abc
52 str,
53 Doc(
54 """
55 The HTTP authorization scheme extracted from the header value.
56 """
57 ),
58 ]
59 credentials: Annotated[ 1abc
60 str,
61 Doc(
62 """
63 The HTTP authorization credentials extracted from the header value.
64 """
65 ),
66 ]
69class HTTPBase(SecurityBase): 1adbc
70 def __init__( 1adbc
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( 1adbc
79 scheme=scheme, description=description
80 )
81 self.scheme_name = scheme_name or self.__class__.__name__ 1adbc
82 self.auto_error = auto_error 1adbc
84 def make_authenticate_headers(self) -> dict[str, str]: 1adbc
85 return {"WWW-Authenticate": f"{self.model.scheme.title()}"} 2wbxbV ybW zbX AbY BbCbdcecDbEbZ Fb0 Gb1 Hb2 IbJbKbLb3 Mb4 Nb5 Ob6 PbQb
87 def make_not_authenticated_error(self) -> HTTPException: 1adbc
88 return HTTPException( 2wbxbz e A 7 f B 8 g V ybW zbX AbY BbCbC D 9 ! h i E F # $ j k dcecDbEbG l H % m I ' n Z Fb0 Gb1 Hb2 IbJbJ K ( ) o p L M * + q r KbLbN s O , t P - u 3 Mb4 Nb5 Ob6 PbQbQ R . / v w S T : ; x y
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: 1adbc
95 authorization = request.headers.get("Authorization") 2wbfcgcxbhcXbicjckcDblcmcEbncYbocKbpcqcLbrcZbsc
96 scheme, credentials = get_authorization_scheme_param(authorization) 2wbfcgcxbhcXbicjckcDblcmcEbncYbocKbpcqcLbrcZbsc
97 if not (authorization and scheme and credentials): 2wbfcgcxbhcXbicjckcDblcmcEbncYbocKbpcqcLbrcZbsc
98 if self.auto_error: 2wbxbXbdcecDbEbYbKbLbZb
99 raise self.make_not_authenticated_error() 2wbxbdcecDbEbKbLb
100 else:
101 return None 2Xb#cYbZb
102 return HTTPAuthorizationCredentials(scheme=scheme, credentials=credentials) 2fcgchcicjckclcmcncocpcqcrcsc
105class HTTPBasic(HTTPBase): 1adbc
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__( 1adbc
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) 2a tcucvcwcxcyczcAcd b BcCcDcEcFcGcHcIcc JcKcLcMcNcOcPcQcRcScTc
193 self.scheme_name = scheme_name or self.__class__.__name__ 2a tcucvcwcxcyczcAcd b BcCcDcEcFcGcHcIcc JcKcLcMcNcOcPcQcRcScTc
194 self.realm = realm 2a tcucvcwcxcyczcAcd b BcCcDcEcFcGcHcIcc JcKcLcMcNcOcPcQcRcScTc
195 self.auto_error = auto_error 2a tcucvcwcxcyczcAcd b BcCcDcEcFcGcHcIcc JcKcLcMcNcOcPcQcRcScTc
197 def make_authenticate_headers(self) -> dict[str, str]: 1adbc
198 if self.realm: 2z e A 7 f B 8 g C D 9 ! h i E F # $ j k U UcG l H % m I ' n J K ( ) o p L M * + q r N s O , t P - u Q R . / v w S T : ; x y
199 return {"WWW-Authenticate": f'Basic realm="{self.realm}"'} 2A 7 f B 8 g VcWcH % m I ' n O , t P - u
200 return {"WWW-Authenticate": "Basic"} 2z e C D 9 ! h i E F # $ j k U UcG l J K ( ) o p L M * + q r N s Q R . / v w S T : ; x y
202 async def __call__( # type: ignore 1adbc
203 self, request: Request
204 ) -> HTTPBasicCredentials | None:
205 authorization = request.headers.get("Authorization") 2z 0be = A 7 f ? B 8 g @ [ ] C D 9 ! h i ^ _ E F ` { | } # $ j k U ~ G 1bl abH % m bbI ' n cbdbebJ K ( ) o p fbgbL M hbibjbkb* + q r N 2bs lbO , t mbP - u nbobpbQ R . / v w qbrbS T sbtbubvb: ; x y
206 scheme, param = get_authorization_scheme_param(authorization) 2z 0be = A 7 f ? B 8 g @ [ ] C D 9 ! h i ^ _ E F ` { | } # $ j k U ~ G 1bl abH % m bbI ' n cbdbebJ K ( ) o p fbgbL M hbibjbkb* + q r N 2bs lbO , t mbP - u nbobpbQ R . / v w qbrbS T sbtbubvb: ; x y
207 if not authorization or scheme.lower() != "basic": 2z 0be = A 7 f ? B 8 g @ [ ] C D 9 ! h i ^ _ E F ` { | } # $ j k U ~ G 1bl abH % m bbI ' n cbdbebJ K ( ) o p fbgbL M hbibjbkb* + q r N 2bs lbO , t mbP - u nbobpbQ R . / v w qbrbS T sbtbubvb: ; x y
208 if self.auto_error: 20b7 8 9 ! # $ 4cWc1b% ' ( ) * + 2b, - . / : ;
209 raise self.make_not_authenticated_error() 27 8 9 ! # $ Wc$c% ' ( ) * + , - . / : ;
210 else:
211 return None 20b4c1b2b
212 try: 2z e = A f ? B g @ [ ] C D h i ^ _ E F ` { | } j k U ~ G l abH m bbI n cbdbebJ K o p fbgbL M hbibjbkbq r N s lbO t mbP u nbobpbQ R v w qbrbS T sbtbubvbx y
213 data = b64decode(param).decode("ascii") 2z e = A f ? B g @ [ ] C D h i ^ _ E F ` { | } j k U ~ G l abH m bbI n cbdbebJ K o p fbgbL M hbibjbkbq r N s lbO t mbP u nbobpbQ R v w qbrbS T sbtbubvbx y
214 except (ValueError, UnicodeDecodeError, binascii.Error) as e: 2z A B C D E F U VcG H I J K L M N O P Q R S T
215 raise self.make_not_authenticated_error() from e 2z A B C D E F U VcG H I J K L M N O P Q R S T
216 username, separator, password = data.partition(":") 2e = f ? g @ [ ] h i ^ _ ` { | } j k ~ Xcl abm bbn cbdbebo p fbgbhbibjbkbq r s lbt mbu nbobpbv w qbrbsbtbubvbx y
217 if not separator: 2e = f ? g @ [ ] h i ^ _ ` { | } j k ~ Xcl abm bbn cbdbebo p fbgbhbibjbkbq r s lbt mbu nbobpbv w qbrbsbtbubvbx y
218 raise self.make_not_authenticated_error() 2e f g h i j k UcXcl m n o p q r s t u v w x y
219 return HTTPBasicCredentials(username=username, password=password) 2= ? @ [ ] ^ _ ` { | } ~ %cabbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvb
222class HTTPBearer(HTTPBase): 1adbc
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__( 1adbc
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) 2a YcZcd b 0c1cc 2c
300 self.scheme_name = scheme_name or self.__class__.__name__ 2a YcZcd b 0c1cc 2c
301 self.auto_error = auto_error 2a YcZcd b 0c1cc 2c
303 async def __call__(self, request: Request) -> HTTPAuthorizationCredentials | None: 1adbc
304 authorization = request.headers.get("Authorization") 2V yb3bW zb4bRb5b6bCb7b8b9b!b#bZ Fb$b0 Gb%bSb'b(bJb)b*b+b3 Mb,b4 Nb-bTb.b/bQb:b;b=b
305 scheme, credentials = get_authorization_scheme_param(authorization) 2V yb3bW zb4bRb5b6bCb7b8b9b!b#bZ Fb$b0 Gb%bSb'b(bJb)b*b+b3 Mb,b4 Nb-bTb.b/bQb:b;b=b
306 if not (authorization and scheme and credentials): 2V yb3bW zb4bRb5b6bCb7b8b9b!b#bZ Fb$b0 Gb%bSb'b(bJb)b*b+b3 Mb,b4 Nb-bTb.b/bQb:b;b=b
307 if self.auto_error: 2ybzb5bCb9b!b5cFbGb'bJb+bMbNb.bQb=b
308 raise self.make_not_authenticated_error() 2ybzbCb9b!b5cFbGbJb+bMbNbQb=b
309 else:
310 return None 25b'c'b.b
311 if scheme.lower() != "bearer": 2V 3bW 4bRb6b7b8b#b3cZ $b0 %bSb(b)b*b3 ,b4 -bTb/b:b;b
312 if self.auto_error: 2V W Rb6c3cZ 0 Sb3 4 Tb
313 raise self.make_not_authenticated_error() 2V W 6c3cZ 0 3 4
314 else:
315 return None 2Rb(cSbTb
316 return HTTPAuthorizationCredentials(scheme=scheme, credentials=credentials) 23b4b6b7b8b#b$b%b(b)b*b,b-b/b:b;b
319class HTTPDigest(HTTPBase): 1adbc
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__( 1adbc
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) 1adbc
401 self.scheme_name = scheme_name or self.__class__.__name__ 1adbc
402 self.auto_error = auto_error 1adbc
404 async def __call__(self, request: Request) -> HTTPAuthorizationCredentials | None: 1adbc
405 authorization = request.headers.get("Authorization") 2X Ab?bY Bb@bUb[b]b^b_b1 Hb`b2 Ib{bVb|b}b5 Ob~b6 PbacWbbccc
406 scheme, credentials = get_authorization_scheme_param(authorization) 2X Ab?bY Bb@bUb[b]b^b_b1 Hb`b2 Ib{bVb|b}b5 Ob~b6 PbacWbbccc
407 if not (authorization and scheme and credentials): 2X Ab?bY Bb@bUb[b]b^b_b1 Hb`b2 Ib{bVb|b}b5 Ob~b6 PbacWbbccc
408 if self.auto_error: 2AbBb[b^b7cHbIb|bObPbbc
409 raise self.make_not_authenticated_error() 2AbBb^bHbIbObPb
410 else:
411 return None 2[b7c|bbc
412 if scheme.lower() != "digest": 2X ?bY @bUb]b_b8c1 `b2 {bVb}b5 ~b6 acWbcc
413 if self.auto_error: 2X Y Ub9c!c1 2 Vb5 6 Wb
414 raise self.make_not_authenticated_error() 2X Y 9c1 2 5 6
415 else:
416 return None 2Ub!cVbWb
417 return HTTPAuthorizationCredentials(scheme=scheme, credentials=credentials) 2?b@b]b_b8c`b{b}b~baccc