Coverage for fastapi/security/http.py: 100%
88 statements
« prev ^ index » next coverage.py v7.6.1, created at 2024-08-08 03:53 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2024-08-08 03:53 +0000
1import binascii 1abcde
2from base64 import b64decode 1abcde
3from typing import Optional 1abcde
5from fastapi.exceptions import HTTPException 1abcde
6from fastapi.openapi.models import HTTPBase as HTTPBaseModel 1abcde
7from fastapi.openapi.models import HTTPBearer as HTTPBearerModel 1abcde
8from fastapi.security.base import SecurityBase 1abcde
9from fastapi.security.utils import get_authorization_scheme_param 1abcde
10from pydantic import BaseModel 1abcde
11from starlette.requests import Request 1abcde
12from starlette.status import HTTP_401_UNAUTHORIZED, HTTP_403_FORBIDDEN 1abcde
13from typing_extensions import Annotated, Doc 1abcde
16class HTTPBasicCredentials(BaseModel): 1abcde
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.")] 1abcde
26 password: Annotated[str, Doc("The HTTP Basic password.")] 1abcde
29class HTTPAuthorizationCredentials(BaseModel): 1abcde
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[ 1abcde
52 str,
53 Doc(
54 """
55 The HTTP authorization scheme extracted from the header value.
56 """
57 ),
58 ]
59 credentials: Annotated[ 1abcde
60 str,
61 Doc(
62 """
63 The HTTP authorization credentials extracted from the header value.
64 """
65 ),
66 ]
69class HTTPBase(SecurityBase): 1abcde
70 def __init__( 1abcde
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) 1abcde
79 self.scheme_name = scheme_name or self.__class__.__name__ 1abcde
80 self.auto_error = auto_error 1abcde
82 async def __call__( 1abcde
83 self, request: Request
84 ) -> Optional[HTTPAuthorizationCredentials]:
85 authorization = request.headers.get("Authorization") 1abcde
86 scheme, credentials = get_authorization_scheme_param(authorization) 1abcde
87 if not (authorization and scheme and credentials): 1abcde
88 if self.auto_error: 1abcde
89 raise HTTPException( 1abcde
90 status_code=HTTP_403_FORBIDDEN, detail="Not authenticated"
91 )
92 else:
93 return None 1abcde
94 return HTTPAuthorizationCredentials(scheme=scheme, credentials=credentials) 1abcde
97class HTTPBasic(HTTPBase): 1abcde
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__( 1abcde
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) 1abcde
183 self.scheme_name = scheme_name or self.__class__.__name__ 1abcde
184 self.realm = realm 1abcde
185 self.auto_error = auto_error 1abcde
187 async def __call__( # type: ignore 1abcde
188 self, request: Request
189 ) -> Optional[HTTPBasicCredentials]:
190 authorization = request.headers.get("Authorization") 1abcde
191 scheme, param = get_authorization_scheme_param(authorization) 1abcde
192 if self.realm: 1abcde
193 unauthorized_headers = {"WWW-Authenticate": f'Basic realm="{self.realm}"'} 1abcde
194 else:
195 unauthorized_headers = {"WWW-Authenticate": "Basic"} 1abcde
196 if not authorization or scheme.lower() != "basic": 1abcde
197 if self.auto_error: 1abcde
198 raise HTTPException( 1abcde
199 status_code=HTTP_401_UNAUTHORIZED,
200 detail="Not authenticated",
201 headers=unauthorized_headers,
202 )
203 else:
204 return None 1abcde
205 invalid_user_credentials_exc = HTTPException( 1abcde
206 status_code=HTTP_401_UNAUTHORIZED,
207 detail="Invalid authentication credentials",
208 headers=unauthorized_headers,
209 )
210 try: 1abcde
211 data = b64decode(param).decode("ascii") 1abcde
212 except (ValueError, UnicodeDecodeError, binascii.Error): 1abcde
213 raise invalid_user_credentials_exc # noqa: B904 1abcde
214 username, separator, password = data.partition(":") 1abcde
215 if not separator: 1abcde
216 raise invalid_user_credentials_exc 1abcde
217 return HTTPBasicCredentials(username=username, password=password) 1abcde
220class HTTPBearer(HTTPBase): 1abcde
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__( 1abcde
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 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) 1abcde
298 self.scheme_name = scheme_name or self.__class__.__name__ 1abcde
299 self.auto_error = auto_error 1abcde
301 async def __call__( 1abcde
302 self, request: Request
303 ) -> Optional[HTTPAuthorizationCredentials]:
304 authorization = request.headers.get("Authorization") 1abcde
305 scheme, credentials = get_authorization_scheme_param(authorization) 1abcde
306 if not (authorization and scheme and credentials): 1abcde
307 if self.auto_error: 1abcde
308 raise HTTPException( 1abcde
309 status_code=HTTP_403_FORBIDDEN, detail="Not authenticated"
310 )
311 else:
312 return None 1abcde
313 if scheme.lower() != "bearer": 1abcde
314 if self.auto_error: 1abcde
315 raise HTTPException( 1abcde
316 status_code=HTTP_403_FORBIDDEN,
317 detail="Invalid authentication credentials",
318 )
319 else:
320 return None 1abcde
321 return HTTPAuthorizationCredentials(scheme=scheme, credentials=credentials) 1abcde
324class HTTPDigest(HTTPBase): 1abcde
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__( 1abcde
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 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) 1abcde
400 self.scheme_name = scheme_name or self.__class__.__name__ 1abcde
401 self.auto_error = auto_error 1abcde
403 async def __call__( 1abcde
404 self, request: Request
405 ) -> Optional[HTTPAuthorizationCredentials]:
406 authorization = request.headers.get("Authorization") 1abcde
407 scheme, credentials = get_authorization_scheme_param(authorization) 1abcde
408 if not (authorization and scheme and credentials): 1abcde
409 if self.auto_error: 1abcde
410 raise HTTPException( 1abcde
411 status_code=HTTP_403_FORBIDDEN, detail="Not authenticated"
412 )
413 else:
414 return None 1abcde
415 if scheme.lower() != "digest": 1abcde
416 raise HTTPException( 1abcde
417 status_code=HTTP_403_FORBIDDEN,
418 detail="Invalid authentication credentials",
419 )
420 return HTTPAuthorizationCredentials(scheme=scheme, credentials=credentials) 1abcde