Coverage for fastapi / security / http.py: 100%

94 statements  

« prev     ^ index     » next       coverage.py v7.13.3, created at 2026-02-12 18:15 +0000

1import binascii 1abcd

2from base64 import b64decode 1abcd

3from typing import Annotated 1abcd

4 

5from annotated_doc import Doc 1abcd

6from fastapi.exceptions import HTTPException 1abcd

7from fastapi.openapi.models import HTTPBase as HTTPBaseModel 1abcd

8from fastapi.openapi.models import HTTPBearer as HTTPBearerModel 1abcd

9from fastapi.security.base import SecurityBase 1abcd

10from fastapi.security.utils import get_authorization_scheme_param 1abcd

11from pydantic import BaseModel 1abcd

12from starlette.requests import Request 1abcd

13from starlette.status import HTTP_401_UNAUTHORIZED 1abcd

14 

15 

16class HTTPBasicCredentials(BaseModel): 1abcd

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.")] 1abcd

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

27 

28 

29class HTTPAuthorizationCredentials(BaseModel): 1abcd

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[ 1abcd

52 str, 

53 Doc( 

54 """ 

55 The HTTP authorization scheme extracted from the header value. 

56 """ 

57 ), 

58 ] 

59 credentials: Annotated[ 1abcd

60 str, 

61 Doc( 

62 """ 

63 The HTTP authorization credentials extracted from the header value. 

64 """ 

65 ), 

66 ] 

67 

68 

69class HTTPBase(SecurityBase): 1abcd

70 def __init__( 1abcd

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( 1abcd

79 scheme=scheme, description=description 

80 ) 

81 self.scheme_name = scheme_name or self.__class__.__name__ 1abcd

82 self.auto_error = auto_error 1abcd

83 

84 def make_authenticate_headers(self) -> dict[str, str]: 1abcd

85 return {"WWW-Authenticate": f"{self.model.scheme.title()}"} 2@ [ I ] J ^ K _ L ` { | } M ~ N abO bbP cbdbebfbQ gbR hbS ibT jbkb

86 

87 def make_not_authenticated_error(self) -> HTTPException: 1abcd

88 return HTTPException( 2@ [ t e u U f v V g I ] J ^ K _ L ` { w W h x X i | } y j z Y k A Z l M ~ N abO bbP cbdbB 0 m C 1 n ebfbD o E 2 p F 3 q Q gbR hbS ibT jbkbG 4 r H 5 s

89 status_code=HTTP_401_UNAUTHORIZED, 

90 detail="Not authenticated", 

91 headers=self.make_authenticate_headers(), 

92 ) 

93 

94 async def __call__(self, request: Request) -> HTTPAuthorizationCredentials | None: 1abcd

95 authorization = request.headers.get("Authorization") 24b@ 5b6b[ 7brb8b| 9b!b} #bsb$beb%b'bfb(btb

96 scheme, credentials = get_authorization_scheme_param(authorization) 24b@ 5b6b[ 7brb8b| 9b!b} #bsb$beb%b'bfb(btb

97 if not (authorization and scheme and credentials): 24b@ 5b6b[ 7brb8b| 9b!b} #bsb$beb%b'bfb(btb

98 if self.auto_error: 2@ [ rb| } sbebfbtb

99 raise self.make_not_authenticated_error() 2@ [ | } ebfb

100 else: 

101 return None 2rbsbtb

102 return HTTPAuthorizationCredentials(scheme=scheme, credentials=credentials) 24b5b6b7b8b9b!b#b$b%b'b(b

103 

104 

105class HTTPBasic(HTTPBase): 1abcd

106 """ 

107 HTTP Basic authentication. 

108 

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

110 

111 ## Usage 

112 

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

114 

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

116 `username` and the `password`. 

117 

118 Read more about it in the 

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

120 

121 ## Example 

122 

123 ```python 

124 from typing import Annotated 

125 

126 from fastapi import Depends, FastAPI 

127 from fastapi.security import HTTPBasic, HTTPBasicCredentials 

128 

129 app = FastAPI() 

130 

131 security = HTTPBasic() 

132 

133 

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

139 

140 def __init__( 1abcd

141 self, 

142 *, 

143 scheme_name: Annotated[ 

144 str | None, 

145 Doc( 

146 """ 

147 Security scheme name. 

148 

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. 

166 

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. 

178 

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

182 

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

184 

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) 1abcd

193 self.scheme_name = scheme_name or self.__class__.__name__ 1abcd

194 self.realm = realm 1abcd

195 self.auto_error = auto_error 1abcd

196 

197 def make_authenticate_headers(self) -> dict[str, str]: 1abcd

198 if self.realm: 1teuUfvVgwWhxXiyjzYkAZlB0mC1nDoE2pF3qG4rH5s

199 return {"WWW-Authenticate": f'Basic realm="{self.realm}"'} 1uUfvVgzYkAZlE2pF3q

200 return {"WWW-Authenticate": "Basic"} 1tewWhxXiyjB0mC1nDoG4rH5s

201 

202 async def __call__( # type: ignore 1abcd

203 self, request: Request 

204 ) -> HTTPBasicCredentials | None: 

205 authorization = request.headers.get("Authorization") 26 t ube 7 u U f 8 v V g 9 w W h ! x # $ X i % y vbj ' z Y k ( A Z l ) B 0 m * C + , 1 n - D wbo . E 2 p / F 3 q : G 4 r ; H = ? 5 s

206 scheme, param = get_authorization_scheme_param(authorization) 26 t ube 7 u U f 8 v V g 9 w W h ! x # $ X i % y vbj ' z Y k ( A Z l ) B 0 m * C + , 1 n - D wbo . E 2 p / F 3 q : G 4 r ; H = ? 5 s

207 if not authorization or scheme.lower() != "basic": 26 t ube 7 u U f 8 v V g 9 w W h ! x # $ X i % y vbj ' z Y k ( A Z l ) B 0 m * C + , 1 n - D wbo . E 2 p / F 3 q : G 4 r ; H = ? 5 s

208 if self.auto_error: 2ubU V W X vbY Z 0 1 wb2 3 4 5

209 raise self.make_not_authenticated_error() 1UVWXYZ012345

210 else: 

211 return None 2ubvbwb

212 try: 16te7uf8vg9wh!x#$i%yj'zk(Al)Bm*C+,n-Do.Ep/Fq:Gr;H=?s

213 data = b64decode(param).decode("ascii") 16te7uf8vg9wh!x#$i%yj'zk(Al)Bm*C+,n-Do.Ep/Fq:Gr;H=?s

214 except (ValueError, UnicodeDecodeError, binascii.Error) as e: 1tuvwxyzABCDEFGH

215 raise self.make_not_authenticated_error() from e 1tuvwxyzABCDEFGH

216 username, separator, password = data.partition(":") 16e7f8g9h!#$i%j'k(l)m*+,n-o.p/q:r;=?s

217 if not separator: 16e7f8g9h!#$i%j'k(l)m*+,n-o.p/q:r;=?s

218 raise self.make_not_authenticated_error() 1efghijklmnopqrs

219 return HTTPBasicCredentials(username=username, password=password) 16789!#$%'()*+,-./:;=?

220 

221 

222class HTTPBearer(HTTPBase): 1abcd

223 """ 

224 HTTP Bearer token authentication. 

225 

226 ## Usage 

227 

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

229 

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

231 the `scheme` and the `credentials`. 

232 

233 ## Example 

234 

235 ```python 

236 from typing import Annotated 

237 

238 from fastapi import Depends, FastAPI 

239 from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer 

240 

241 app = FastAPI() 

242 

243 security = HTTPBearer() 

244 

245 

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

253 

254 def __init__( 1abcd

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. 

263 

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. 

273 

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. 

285 

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

289 

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

291 

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) 1abcd

300 self.scheme_name = scheme_name or self.__class__.__name__ 1abcd

301 self.auto_error = auto_error 1abcd

302 

303 async def __call__(self, request: Request) -> HTTPAuthorizationCredentials | None: 1abcd

304 authorization = request.headers.get("Authorization") 2xbI ] ybJ ^ zblbAbBb{ CbDbEbM ~ FbN abGbmbHbIbdbJbKbLbQ gbMbR hbNbnbObPbkbQbRb

305 scheme, credentials = get_authorization_scheme_param(authorization) 2xbI ] ybJ ^ zblbAbBb{ CbDbEbM ~ FbN abGbmbHbIbdbJbKbLbQ gbMbR hbNbnbObPbkbQbRb

306 if not (authorization and scheme and credentials): 2xbI ] ybJ ^ zblbAbBb{ CbDbEbM ~ FbN abGbmbHbIbdbJbKbLbQ gbMbR hbNbnbObPbkbQbRb

307 if self.auto_error: 2] ^ Ab{ Db~ abHbdbKbgbhbObkbRb

308 raise self.make_not_authenticated_error() 2] ^ { Db~ abdbKbgbhbkbRb

309 else: 

310 return None 2AbHbOb

311 if scheme.lower() != "bearer": 2xbI ybJ zblbBbCbEbM FbN GbmbIbJbLbQ MbR NbnbPbQb

312 if self.auto_error: 2I J lbM N mbQ R nb

313 raise self.make_not_authenticated_error() 1IJMNQR

314 else: 

315 return None 2lbmbnb

316 return HTTPAuthorizationCredentials(scheme=scheme, credentials=credentials) 2xbybzbBbCbEbFbGbIbJbLbMbNbPbQb

317 

318 

319class HTTPDigest(HTTPBase): 1abcd

320 """ 

321 HTTP Digest authentication. 

322 

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. 

326 

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

328 

329 ## Usage 

330 

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

332 

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

334 the `scheme` and the `credentials`. 

335 

336 ## Example 

337 

338 ```python 

339 from typing import Annotated 

340 

341 from fastapi import Depends, FastAPI 

342 from fastapi.security import HTTPAuthorizationCredentials, HTTPDigest 

343 

344 app = FastAPI() 

345 

346 security = HTTPDigest() 

347 

348 

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

356 

357 def __init__( 1abcd

358 self, 

359 *, 

360 scheme_name: Annotated[ 

361 str | None, 

362 Doc( 

363 """ 

364 Security scheme name. 

365 

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. 

375 

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. 

386 

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

390 

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

392 

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) 1abcd

401 self.scheme_name = scheme_name or self.__class__.__name__ 1abcd

402 self.auto_error = auto_error 1abcd

403 

404 async def __call__(self, request: Request) -> HTTPAuthorizationCredentials | None: 1abcd

405 authorization = request.headers.get("Authorization") 2SbK _ TbL ` UbobVbWbO bbXbP cbYbpbZb0bS ib1bT jb2bqb3b

406 scheme, credentials = get_authorization_scheme_param(authorization) 2SbK _ TbL ` UbobVbWbO bbXbP cbYbpbZb0bS ib1bT jb2bqb3b

407 if not (authorization and scheme and credentials): 2SbK _ TbL ` UbobVbWbO bbXbP cbYbpbZb0bS ib1bT jb2bqb3b

408 if self.auto_error: 2_ ` VbbbcbZbibjb3b

409 raise self.make_not_authenticated_error() 2_ ` bbcbibjb

410 else: 

411 return None 2VbZb3b

412 if scheme.lower() != "digest": 2SbK TbL UbobWbO XbP Ybpb0bS 1bT 2bqb

413 if self.auto_error: 2K L obO P pbS T qb

414 raise self.make_not_authenticated_error() 1KLOPST

415 else: 

416 return None 2obpbqb

417 return HTTPAuthorizationCredentials(scheme=scheme, credentials=credentials) 2SbTbUbWbXbYb0b1b2b