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

1import binascii 1adbc

2from base64 import b64decode 1adbc

3from typing import Annotated 1adbc

4 

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

14 

15 

16class HTTPBasicCredentials(BaseModel): 1adbc

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

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

27 

28 

29class HTTPAuthorizationCredentials(BaseModel): 1adbc

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[ 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 ] 

67 

68 

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

83 

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

86 

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 ) 

93 

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

103 

104 

105class HTTPBasic(HTTPBase): 1adbc

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__( 1adbc

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

196 

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

201 

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

220 

221 

222class HTTPBearer(HTTPBase): 1adbc

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__( 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. 

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

302 

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

317 

318 

319class HTTPDigest(HTTPBase): 1adbc

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__( 1adbc

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

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

402 self.auto_error = auto_error 1adbc

403 

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