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

1import binascii 1abcdef

2from base64 import b64decode 1abcdef

3from typing import Optional 1abcdef

4 

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

14 

15 

16class HTTPBasicCredentials(BaseModel): 1abcdef

17 """ 

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

19 dependency. 

20 

21 Read more about it in the 

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

23 """ 

24 

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

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

27 

28 

29class HTTPAuthorizationCredentials(BaseModel): 1abcdef

30 """ 

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

32 `HTTPDigest` in a dependency. 

33 

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

35 

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

37 

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

39 like: 

40 

41 ``` 

42 Authorization: Bearer deadbeef12346 

43 ``` 

44 

45 In this case: 

46 

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

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

49 """ 

50 

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 ] 

67 

68 

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

81 

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

95 

96 

97class HTTPBasic(HTTPBase): 1abcdef

98 """ 

99 HTTP Basic authentication. 

100 

101 ## Usage 

102 

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

104 

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

106 `username` and the `password`. 

107 

108 Read more about it in the 

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

110 

111 ## Example 

112 

113 ```python 

114 from typing import Annotated 

115 

116 from fastapi import Depends, FastAPI 

117 from fastapi.security import HTTPBasic, HTTPBasicCredentials 

118 

119 app = FastAPI() 

120 

121 security = HTTPBasic() 

122 

123 

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

129 

130 def __init__( 1abcdef

131 self, 

132 *, 

133 scheme_name: Annotated[ 

134 Optional[str], 

135 Doc( 

136 """ 

137 Security scheme name. 

138 

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. 

156 

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. 

168 

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

172 

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

174 

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

186 

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

218 

219 

220class HTTPBearer(HTTPBase): 1abcdef

221 """ 

222 HTTP Bearer token authentication. 

223 

224 ## Usage 

225 

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

227 

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

229 the `scheme` and the `credentials`. 

230 

231 ## Example 

232 

233 ```python 

234 from typing import Annotated 

235 

236 from fastapi import Depends, FastAPI 

237 from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer 

238 

239 app = FastAPI() 

240 

241 security = HTTPBearer() 

242 

243 

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

251 

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. 

261 

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. 

271 

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. 

283 

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

287 

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

289 

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

300 

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

322 

323 

324class HTTPDigest(HTTPBase): 1abcdef

325 """ 

326 HTTP Digest authentication. 

327 

328 ## Usage 

329 

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

331 

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

333 the `scheme` and the `credentials`. 

334 

335 ## Example 

336 

337 ```python 

338 from typing import Annotated 

339 

340 from fastapi import Depends, FastAPI 

341 from fastapi.security import HTTPAuthorizationCredentials, HTTPDigest 

342 

343 app = FastAPI() 

344 

345 security = HTTPDigest() 

346 

347 

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

355 

356 def __init__( 1abcdef

357 self, 

358 *, 

359 scheme_name: Annotated[ 

360 Optional[str], 

361 Doc( 

362 """ 

363 Security scheme name. 

364 

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. 

374 

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. 

385 

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

389 

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

391 

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

402 

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