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

1import binascii 1abcdefg

2from base64 import b64decode 1abcdefg

3from typing import Dict, Optional 1abcdefg

4 

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

15 

16 

17class HTTPBasicCredentials(BaseModel): 1abcdefg

18 """ 

19 The HTTP Basic credentials given as the result of using `HTTPBasic` in a 

20 dependency. 

21 

22 Read more about it in the 

23 [FastAPI docs for HTTP Basic Auth](https://fastapi.tiangolo.com/advanced/security/http-basic-auth/). 

24 """ 

25 

26 username: Annotated[str, Doc("The HTTP Basic username.")] 1abcdefg

27 password: Annotated[str, Doc("The HTTP Basic password.")] 1abcdefg

28 

29 

30class HTTPAuthorizationCredentials(BaseModel): 1abcdefg

31 """ 

32 The HTTP authorization credentials in the result of using `HTTPBearer` or 

33 `HTTPDigest` in a dependency. 

34 

35 The HTTP authorization header value is split by the first space. 

36 

37 The first part is the `scheme`, the second part is the `credentials`. 

38 

39 For example, in an HTTP Bearer token scheme, the client will send a header 

40 like: 

41 

42 ``` 

43 Authorization: Bearer deadbeef12346 

44 ``` 

45 

46 In this case: 

47 

48 * `scheme` will have the value `"Bearer"` 

49 * `credentials` will have the value `"deadbeef12346"` 

50 """ 

51 

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 ] 

68 

69 

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

84 

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

87 

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 ) 

94 

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

106 

107 

108class HTTPBasic(HTTPBase): 1abcdefg

109 """ 

110 HTTP Basic authentication. 

111 

112 Ref: https://datatracker.ietf.org/doc/html/rfc7617 

113 

114 ## Usage 

115 

116 Create an instance object and use that object as the dependency in `Depends()`. 

117 

118 The dependency result will be an `HTTPBasicCredentials` object containing the 

119 `username` and the `password`. 

120 

121 Read more about it in the 

122 [FastAPI docs for HTTP Basic Auth](https://fastapi.tiangolo.com/advanced/security/http-basic-auth/). 

123 

124 ## Example 

125 

126 ```python 

127 from typing import Annotated 

128 

129 from fastapi import Depends, FastAPI 

130 from fastapi.security import HTTPBasic, HTTPBasicCredentials 

131 

132 app = FastAPI() 

133 

134 security = HTTPBasic() 

135 

136 

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 """ 

142 

143 def __init__( 1abcdefg

144 self, 

145 *, 

146 scheme_name: Annotated[ 

147 Optional[str], 

148 Doc( 

149 """ 

150 Security scheme name. 

151 

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. 

169 

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. 

181 

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`. 

185 

186 This is useful when you want to have optional authentication. 

187 

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

199 

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

204 

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

223 

224 

225class HTTPBearer(HTTPBase): 1abcdefg

226 """ 

227 HTTP Bearer token authentication. 

228 

229 ## Usage 

230 

231 Create an instance object and use that object as the dependency in `Depends()`. 

232 

233 The dependency result will be an `HTTPAuthorizationCredentials` object containing 

234 the `scheme` and the `credentials`. 

235 

236 ## Example 

237 

238 ```python 

239 from typing import Annotated 

240 

241 from fastapi import Depends, FastAPI 

242 from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer 

243 

244 app = FastAPI() 

245 

246 security = HTTPBearer() 

247 

248 

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 """ 

256 

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. 

266 

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. 

276 

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. 

288 

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`. 

292 

293 This is useful when you want to have optional authentication. 

294 

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

305 

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

322 

323 

324class HTTPDigest(HTTPBase): 1abcdefg

325 """ 

326 HTTP Digest authentication. 

327 

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. 

331 

332 Ref: https://datatracker.ietf.org/doc/html/rfc7616 

333 

334 ## Usage 

335 

336 Create an instance object and use that object as the dependency in `Depends()`. 

337 

338 The dependency result will be an `HTTPAuthorizationCredentials` object containing 

339 the `scheme` and the `credentials`. 

340 

341 ## Example 

342 

343 ```python 

344 from typing import Annotated 

345 

346 from fastapi import Depends, FastAPI 

347 from fastapi.security import HTTPAuthorizationCredentials, HTTPDigest 

348 

349 app = FastAPI() 

350 

351 security = HTTPDigest() 

352 

353 

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 """ 

361 

362 def __init__( 1abcdefg

363 self, 

364 *, 

365 scheme_name: Annotated[ 

366 Optional[str], 

367 Doc( 

368 """ 

369 Security scheme name. 

370 

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. 

380 

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. 

391 

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`. 

395 

396 This is useful when you want to have optional authentication. 

397 

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

408 

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