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

64 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2025-09-09 09:16 +0000

1from typing import Any, Dict, List, Optional, Union, cast 1abcdef

2 

3from fastapi.exceptions import HTTPException 1abcdef

4from fastapi.openapi.models import OAuth2 as OAuth2Model 1abcdef

5from fastapi.openapi.models import OAuthFlows as OAuthFlowsModel 1abcdef

6from fastapi.param_functions import Form 1abcdef

7from fastapi.security.base import SecurityBase 1abcdef

8from fastapi.security.utils import get_authorization_scheme_param 1abcdef

9from starlette.requests import Request 1abcdef

10from starlette.status import HTTP_401_UNAUTHORIZED, HTTP_403_FORBIDDEN 1abcdef

11 

12# TODO: import from typing when deprecating Python 3.9 

13from typing_extensions import Annotated, Doc 1abcdef

14 

15 

16class OAuth2PasswordRequestForm: 1abcdef

17 """ 

18 This is a dependency class to collect the `username` and `password` as form data 

19 for an OAuth2 password flow. 

20 

21 The OAuth2 specification dictates that for a password flow the data should be 

22 collected using form data (instead of JSON) and that it should have the specific 

23 fields `username` and `password`. 

24 

25 All the initialization parameters are extracted from the request. 

26 

27 Read more about it in the 

28 [FastAPI docs for Simple OAuth2 with Password and Bearer](https://fastapi.tiangolo.com/tutorial/security/simple-oauth2/). 

29 

30 ## Example 

31 

32 ```python 

33 from typing import Annotated 

34 

35 from fastapi import Depends, FastAPI 

36 from fastapi.security import OAuth2PasswordRequestForm 

37 

38 app = FastAPI() 

39 

40 

41 @app.post("/login") 

42 def login(form_data: Annotated[OAuth2PasswordRequestForm, Depends()]): 

43 data = {} 

44 data["scopes"] = [] 

45 for scope in form_data.scopes: 

46 data["scopes"].append(scope) 

47 if form_data.client_id: 

48 data["client_id"] = form_data.client_id 

49 if form_data.client_secret: 

50 data["client_secret"] = form_data.client_secret 

51 return data 

52 ``` 

53 

54 Note that for OAuth2 the scope `items:read` is a single scope in an opaque string. 

55 You could have custom internal logic to separate it by colon characters (`:`) or 

56 similar, and get the two parts `items` and `read`. Many applications do that to 

57 group and organize permissions, you could do it as well in your application, just 

58 know that that it is application specific, it's not part of the specification. 

59 """ 

60 

61 def __init__( 1abcdef

62 self, 

63 *, 

64 grant_type: Annotated[ 

65 Union[str, None], 

66 Form(pattern="^password$"), 

67 Doc( 

68 """ 

69 The OAuth2 spec says it is required and MUST be the fixed string 

70 "password". Nevertheless, this dependency class is permissive and 

71 allows not passing it. If you want to enforce it, use instead the 

72 `OAuth2PasswordRequestFormStrict` dependency. 

73 """ 

74 ), 

75 ] = None, 

76 username: Annotated[ 

77 str, 

78 Form(), 

79 Doc( 

80 """ 

81 `username` string. The OAuth2 spec requires the exact field name 

82 `username`. 

83 """ 

84 ), 

85 ], 

86 password: Annotated[ 

87 str, 

88 Form(json_schema_extra={"format": "password"}), 

89 Doc( 

90 """ 

91 `password` string. The OAuth2 spec requires the exact field name 

92 `password". 

93 """ 

94 ), 

95 ], 

96 scope: Annotated[ 

97 str, 

98 Form(), 

99 Doc( 

100 """ 

101 A single string with actually several scopes separated by spaces. Each 

102 scope is also a string. 

103 

104 For example, a single string with: 

105 

106 ```python 

107 "items:read items:write users:read profile openid" 

108 ```` 

109 

110 would represent the scopes: 

111 

112 * `items:read` 

113 * `items:write` 

114 * `users:read` 

115 * `profile` 

116 * `openid` 

117 """ 

118 ), 

119 ] = "", 

120 client_id: Annotated[ 

121 Union[str, None], 

122 Form(), 

123 Doc( 

124 """ 

125 If there's a `client_id`, it can be sent as part of the form fields. 

126 But the OAuth2 specification recommends sending the `client_id` and 

127 `client_secret` (if any) using HTTP Basic auth. 

128 """ 

129 ), 

130 ] = None, 

131 client_secret: Annotated[ 

132 Union[str, None], 

133 Form(json_schema_extra={"format": "password"}), 

134 Doc( 

135 """ 

136 If there's a `client_password` (and a `client_id`), they can be sent 

137 as part of the form fields. But the OAuth2 specification recommends 

138 sending the `client_id` and `client_secret` (if any) using HTTP Basic 

139 auth. 

140 """ 

141 ), 

142 ] = None, 

143 ): 

144 self.grant_type = grant_type 2K L M 2 3 4 5 6 7 g h i j k N O P 8 9 ! # $ % l m n o p Q R S ' ( ) * + , q r s t u T U V - . / : ; = v w x y z W X Y ? @ [ ] ^ _ A B C D E Z 0 1 ` { | } ~ abF G H I J

145 self.username = username 2K L M 2 3 4 5 6 7 g h i j k N O P 8 9 ! # $ % l m n o p Q R S ' ( ) * + , q r s t u T U V - . / : ; = v w x y z W X Y ? @ [ ] ^ _ A B C D E Z 0 1 ` { | } ~ abF G H I J

146 self.password = password 2K L M 2 3 4 5 6 7 g h i j k N O P 8 9 ! # $ % l m n o p Q R S ' ( ) * + , q r s t u T U V - . / : ; = v w x y z W X Y ? @ [ ] ^ _ A B C D E Z 0 1 ` { | } ~ abF G H I J

147 self.scopes = scope.split() 2K L M 2 3 4 5 6 7 g h i j k N O P 8 9 ! # $ % l m n o p Q R S ' ( ) * + , q r s t u T U V - . / : ; = v w x y z W X Y ? @ [ ] ^ _ A B C D E Z 0 1 ` { | } ~ abF G H I J

148 self.client_id = client_id 2K L M 2 3 4 5 6 7 g h i j k N O P 8 9 ! # $ % l m n o p Q R S ' ( ) * + , q r s t u T U V - . / : ; = v w x y z W X Y ? @ [ ] ^ _ A B C D E Z 0 1 ` { | } ~ abF G H I J

149 self.client_secret = client_secret 2K L M 2 3 4 5 6 7 g h i j k N O P 8 9 ! # $ % l m n o p Q R S ' ( ) * + , q r s t u T U V - . / : ; = v w x y z W X Y ? @ [ ] ^ _ A B C D E Z 0 1 ` { | } ~ abF G H I J

150 

151 

152class OAuth2PasswordRequestFormStrict(OAuth2PasswordRequestForm): 1abcdef

153 """ 

154 This is a dependency class to collect the `username` and `password` as form data 

155 for an OAuth2 password flow. 

156 

157 The OAuth2 specification dictates that for a password flow the data should be 

158 collected using form data (instead of JSON) and that it should have the specific 

159 fields `username` and `password`. 

160 

161 All the initialization parameters are extracted from the request. 

162 

163 The only difference between `OAuth2PasswordRequestFormStrict` and 

164 `OAuth2PasswordRequestForm` is that `OAuth2PasswordRequestFormStrict` requires the 

165 client to send the form field `grant_type` with the value `"password"`, which 

166 is required in the OAuth2 specification (it seems that for no particular reason), 

167 while for `OAuth2PasswordRequestForm` `grant_type` is optional. 

168 

169 Read more about it in the 

170 [FastAPI docs for Simple OAuth2 with Password and Bearer](https://fastapi.tiangolo.com/tutorial/security/simple-oauth2/). 

171 

172 ## Example 

173 

174 ```python 

175 from typing import Annotated 

176 

177 from fastapi import Depends, FastAPI 

178 from fastapi.security import OAuth2PasswordRequestForm 

179 

180 app = FastAPI() 

181 

182 

183 @app.post("/login") 

184 def login(form_data: Annotated[OAuth2PasswordRequestFormStrict, Depends()]): 

185 data = {} 

186 data["scopes"] = [] 

187 for scope in form_data.scopes: 

188 data["scopes"].append(scope) 

189 if form_data.client_id: 

190 data["client_id"] = form_data.client_id 

191 if form_data.client_secret: 

192 data["client_secret"] = form_data.client_secret 

193 return data 

194 ``` 

195 

196 Note that for OAuth2 the scope `items:read` is a single scope in an opaque string. 

197 You could have custom internal logic to separate it by colon characters (`:`) or 

198 similar, and get the two parts `items` and `read`. Many applications do that to 

199 group and organize permissions, you could do it as well in your application, just 

200 know that that it is application specific, it's not part of the specification. 

201 

202 

203 grant_type: the OAuth2 spec says it is required and MUST be the fixed string "password". 

204 This dependency is strict about it. If you want to be permissive, use instead the 

205 OAuth2PasswordRequestForm dependency class. 

206 username: username string. The OAuth2 spec requires the exact field name "username". 

207 password: password string. The OAuth2 spec requires the exact field name "password". 

208 scope: Optional string. Several scopes (each one a string) separated by spaces. E.g. 

209 "items:read items:write users:read profile openid" 

210 client_id: optional string. OAuth2 recommends sending the client_id and client_secret (if any) 

211 using HTTP Basic auth, as: client_id:client_secret 

212 client_secret: optional string. OAuth2 recommends sending the client_id and client_secret (if any) 

213 using HTTP Basic auth, as: client_id:client_secret 

214 """ 

215 

216 def __init__( 1abcdef

217 self, 

218 grant_type: Annotated[ 

219 str, 

220 Form(pattern="^password$"), 

221 Doc( 

222 """ 

223 The OAuth2 spec says it is required and MUST be the fixed string 

224 "password". This dependency is strict about it. If you want to be 

225 permissive, use instead the `OAuth2PasswordRequestForm` dependency 

226 class. 

227 """ 

228 ), 

229 ], 

230 username: Annotated[ 

231 str, 

232 Form(), 

233 Doc( 

234 """ 

235 `username` string. The OAuth2 spec requires the exact field name 

236 `username`. 

237 """ 

238 ), 

239 ], 

240 password: Annotated[ 

241 str, 

242 Form(), 

243 Doc( 

244 """ 

245 `password` string. The OAuth2 spec requires the exact field name 

246 `password". 

247 """ 

248 ), 

249 ], 

250 scope: Annotated[ 

251 str, 

252 Form(), 

253 Doc( 

254 """ 

255 A single string with actually several scopes separated by spaces. Each 

256 scope is also a string. 

257 

258 For example, a single string with: 

259 

260 ```python 

261 "items:read items:write users:read profile openid" 

262 ```` 

263 

264 would represent the scopes: 

265 

266 * `items:read` 

267 * `items:write` 

268 * `users:read` 

269 * `profile` 

270 * `openid` 

271 """ 

272 ), 

273 ] = "", 

274 client_id: Annotated[ 

275 Union[str, None], 

276 Form(), 

277 Doc( 

278 """ 

279 If there's a `client_id`, it can be sent as part of the form fields. 

280 But the OAuth2 specification recommends sending the `client_id` and 

281 `client_secret` (if any) using HTTP Basic auth. 

282 """ 

283 ), 

284 ] = None, 

285 client_secret: Annotated[ 

286 Union[str, None], 

287 Form(), 

288 Doc( 

289 """ 

290 If there's a `client_password` (and a `client_id`), they can be sent 

291 as part of the form fields. But the OAuth2 specification recommends 

292 sending the `client_id` and `client_secret` (if any) using HTTP Basic 

293 auth. 

294 """ 

295 ), 

296 ] = None, 

297 ): 

298 super().__init__( 1KLMNOPQRSTUVWXYZ01

299 grant_type=grant_type, 

300 username=username, 

301 password=password, 

302 scope=scope, 

303 client_id=client_id, 

304 client_secret=client_secret, 

305 ) 

306 

307 

308class OAuth2(SecurityBase): 1abcdef

309 """ 

310 This is the base class for OAuth2 authentication, an instance of it would be used 

311 as a dependency. All other OAuth2 classes inherit from it and customize it for 

312 each OAuth2 flow. 

313 

314 You normally would not create a new class inheriting from it but use one of the 

315 existing subclasses, and maybe compose them if you want to support multiple flows. 

316 

317 Read more about it in the 

318 [FastAPI docs for Security](https://fastapi.tiangolo.com/tutorial/security/). 

319 """ 

320 

321 def __init__( 1abcdef

322 self, 

323 *, 

324 flows: Annotated[ 

325 Union[OAuthFlowsModel, Dict[str, Dict[str, Any]]], 

326 Doc( 

327 """ 

328 The dictionary of OAuth2 flows. 

329 """ 

330 ), 

331 ] = OAuthFlowsModel(), 

332 scheme_name: Annotated[ 

333 Optional[str], 

334 Doc( 

335 """ 

336 Security scheme name. 

337 

338 It will be included in the generated OpenAPI (e.g. visible at `/docs`). 

339 """ 

340 ), 

341 ] = None, 

342 description: Annotated[ 

343 Optional[str], 

344 Doc( 

345 """ 

346 Security scheme description. 

347 

348 It will be included in the generated OpenAPI (e.g. visible at `/docs`). 

349 """ 

350 ), 

351 ] = None, 

352 auto_error: Annotated[ 

353 bool, 

354 Doc( 

355 """ 

356 By default, if no HTTP Authorization header is provided, required for 

357 OAuth2 authentication, it will automatically cancel the request and 

358 send the client an error. 

359 

360 If `auto_error` is set to `False`, when the HTTP Authorization header 

361 is not available, instead of erroring out, the dependency result will 

362 be `None`. 

363 

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

365 

366 It is also useful when you want to have authentication that can be 

367 provided in one of multiple optional ways (for example, with OAuth2 

368 or in a cookie). 

369 """ 

370 ), 

371 ] = True, 

372 ): 

373 self.model = OAuth2Model( 1abcdef

374 flows=cast(OAuthFlowsModel, flows), description=description 

375 ) 

376 self.scheme_name = scheme_name or self.__class__.__name__ 1abcdef

377 self.auto_error = auto_error 1abcdef

378 

379 async def __call__(self, request: Request) -> Optional[str]: 1abcdef

380 authorization = request.headers.get("Authorization") 2ddAcedfdBcgdhdCcidjdDckdldEcmdndFcodpdGcqdrdHcsdtdIcudvdJcwdxdKcydzdLcAdBdMcCdDdNcEdFdOcGdHdPcIdJdQcKdLdRcMd

381 if not authorization: 2ddAcedfdBcgdhdCcidjdDckdldEcmdndFcodpdGcqdrdHcsdtdIcudvdJcwdxdKcydzdLcAdBdMcCdDdNcEdFdOcGdHdPcIdJdQcKdLdRcMd

382 if self.auto_error: 2AcBcCcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRc

383 raise HTTPException( 2AcDcGcJcMcPc

384 status_code=HTTP_403_FORBIDDEN, detail="Not authenticated" 

385 ) 

386 else: 

387 return None 2BcCcEcFcHcIcKcLcNcOcQcRc

388 return authorization 2ddedfdgdhdidjdkdldmdndodpdqdrdsdtdudvdwdxdydzdAdBdCdDdEdFdGdHdIdJdKdLdMd

389 

390 

391class OAuth2PasswordBearer(OAuth2): 1abcdef

392 """ 

393 OAuth2 flow for authentication using a bearer token obtained with a password. 

394 An instance of it would be used as a dependency. 

395 

396 Read more about it in the 

397 [FastAPI docs for Simple OAuth2 with Password and Bearer](https://fastapi.tiangolo.com/tutorial/security/simple-oauth2/). 

398 """ 

399 

400 def __init__( 1abcdef

401 self, 

402 tokenUrl: Annotated[ 

403 str, 

404 Doc( 

405 """ 

406 The URL to obtain the OAuth2 token. This would be the *path operation* 

407 that has `OAuth2PasswordRequestForm` as a dependency. 

408 """ 

409 ), 

410 ], 

411 scheme_name: Annotated[ 

412 Optional[str], 

413 Doc( 

414 """ 

415 Security scheme name. 

416 

417 It will be included in the generated OpenAPI (e.g. visible at `/docs`). 

418 """ 

419 ), 

420 ] = None, 

421 scopes: Annotated[ 

422 Optional[Dict[str, str]], 

423 Doc( 

424 """ 

425 The OAuth2 scopes that would be required by the *path operations* that 

426 use this dependency. 

427 """ 

428 ), 

429 ] = None, 

430 description: Annotated[ 

431 Optional[str], 

432 Doc( 

433 """ 

434 Security scheme description. 

435 

436 It will be included in the generated OpenAPI (e.g. visible at `/docs`). 

437 """ 

438 ), 

439 ] = None, 

440 auto_error: Annotated[ 

441 bool, 

442 Doc( 

443 """ 

444 By default, if no HTTP Authorization header is provided, required for 

445 OAuth2 authentication, it will automatically cancel the request and 

446 send the client an error. 

447 

448 If `auto_error` is set to `False`, when the HTTP Authorization header 

449 is not available, instead of erroring out, the dependency result will 

450 be `None`. 

451 

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

453 

454 It is also useful when you want to have authentication that can be 

455 provided in one of multiple optional ways (for example, with OAuth2 

456 or in a cookie). 

457 """ 

458 ), 

459 ] = True, 

460 refreshUrl: Annotated[ 

461 Optional[str], 

462 Doc( 

463 """ 

464 The URL to refresh the token and obtain a new one. 

465 """ 

466 ), 

467 ] = None, 

468 ): 

469 if not scopes: 1abcdef

470 scopes = {} 1abcdef

471 flows = OAuthFlowsModel( 1abcdef

472 password=cast( 

473 Any, 

474 { 

475 "tokenUrl": tokenUrl, 

476 "refreshUrl": refreshUrl, 

477 "scopes": scopes, 

478 }, 

479 ) 

480 ) 

481 super().__init__( 1abcdef

482 flows=flows, 

483 scheme_name=scheme_name, 

484 description=description, 

485 auto_error=auto_error, 

486 ) 

487 

488 async def __call__(self, request: Request) -> Optional[str]: 1abcdef

489 authorization = request.headers.get("Authorization") 2zbAbScBbCbTcDbEbUcVcWcFbGbXcbbHbIbg h Jbi j k cbdbebKbLbYcMbNbZcObPb0c1c2cQbRb3cfbSbTbl m Ubn o p gbhbibVbWb4cXbYb5cZb0b6c7c8c1b2b9cjb3b4bq r 5bs t u kblbmb6b7b!c8b9b#c!b#b$c%c'c$b%b(cnb'b(bv w )bx y z obpbqb*b+b)c,b-b*c.b/b+c,c-c:b;b.crb=b?bA B @bC D E sbtbub[b]b/c^b_b:c`b{b;c=c?c|b}b@cvb~bacF G bcH I J wbxbyb

490 scheme, param = get_authorization_scheme_param(authorization) 2zbAbScBbCbTcDbEbUcVcWcFbGbXcbbHbIbg h Jbi j k cbdbebKbLbYcMbNbZcObPb0c1c2cQbRb3cfbSbTbl m Ubn o p gbhbibVbWb4cXbYb5cZb0b6c7c8c1b2b9cjb3b4bq r 5bs t u kblbmb6b7b!c8b9b#c!b#b$c%c'c$b%b(cnb'b(bv w )bx y z obpbqb*b+b)c,b-b*c.b/b+c,c-c:b;b.crb=b?bA B @bC D E sbtbub[b]b/c^b_b:c`b{b;c=c?c|b}b@cvb~bacF G bcH I J wbxbyb

491 if not authorization or scheme.lower() != "bearer": 2zbAbScBbCbTcDbEbUcVcWcFbGbXcbbHbIbg h Jbi j k cbdbebKbLbYcMbNbZcObPb0c1c2cQbRb3cfbSbTbl m Ubn o p gbhbibVbWb4cXbYb5cZb0b6c7c8c1b2b9cjb3b4bq r 5bs t u kblbmb6b7b!c8b9b#c!b#b$c%c'c$b%b(cnb'b(bv w )bx y z obpbqb*b+b)c,b-b*c.b/b+c,c-c:b;b.crb=b?bA B @bC D E sbtbub[b]b/c^b_b:c`b{b;c=c?c|b}b@cvb~bacF G bcH I J wbxbyb

492 if self.auto_error: 2zbAbBbCbDbEbFbGbHbIbJbKbLbMbNbObPbQbRbSbTbUbVbWbXbYbZb0b1b2b3b4b5b6b7b8b9b!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~bacbc

493 raise HTTPException( 2DbEbFbGbHbIbJbObPbQbRbSbTbUbZb0b1b2b3b4b5b!b#b$b%b'b(b)b.b/b:b;b=b?b@b`b{b|b}b~bacbc

494 status_code=HTTP_401_UNAUTHORIZED, 

495 detail="Not authenticated", 

496 headers={"WWW-Authenticate": "Bearer"}, 

497 ) 

498 else: 

499 return None 2zbAbBbCbKbLbMbNbVbWbXbYb6b7b8b9b*b+b,b-b[b]b^b_b

500 return param 2ScTcUcVcWcXcbbg h i j k cbdbebYcZc0c1c2c3cfbl m n o p gbhbib4c5c6c7c8c9cjbq r s t u kblbmb!c#c$c%c'c(cnbv w x y z obpbqb)c*c+c,c-c.crbA B C D E sbtbub/c:c;c=c?c@cvbF G H I J wbxbyb

501 

502 

503class OAuth2AuthorizationCodeBearer(OAuth2): 1abcdef

504 """ 

505 OAuth2 flow for authentication using a bearer token obtained with an OAuth2 code 

506 flow. An instance of it would be used as a dependency. 

507 """ 

508 

509 def __init__( 1abcdef

510 self, 

511 authorizationUrl: str, 

512 tokenUrl: Annotated[ 

513 str, 

514 Doc( 

515 """ 

516 The URL to obtain the OAuth2 token. 

517 """ 

518 ), 

519 ], 

520 refreshUrl: Annotated[ 

521 Optional[str], 

522 Doc( 

523 """ 

524 The URL to refresh the token and obtain a new one. 

525 """ 

526 ), 

527 ] = None, 

528 scheme_name: Annotated[ 

529 Optional[str], 

530 Doc( 

531 """ 

532 Security scheme name. 

533 

534 It will be included in the generated OpenAPI (e.g. visible at `/docs`). 

535 """ 

536 ), 

537 ] = None, 

538 scopes: Annotated[ 

539 Optional[Dict[str, str]], 

540 Doc( 

541 """ 

542 The OAuth2 scopes that would be required by the *path operations* that 

543 use this dependency. 

544 """ 

545 ), 

546 ] = None, 

547 description: Annotated[ 

548 Optional[str], 

549 Doc( 

550 """ 

551 Security scheme description. 

552 

553 It will be included in the generated OpenAPI (e.g. visible at `/docs`). 

554 """ 

555 ), 

556 ] = None, 

557 auto_error: Annotated[ 

558 bool, 

559 Doc( 

560 """ 

561 By default, if no HTTP Authorization header is provided, required for 

562 OAuth2 authentication, it will automatically cancel the request and 

563 send the client an error. 

564 

565 If `auto_error` is set to `False`, when the HTTP Authorization header 

566 is not available, instead of erroring out, the dependency result will 

567 be `None`. 

568 

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

570 

571 It is also useful when you want to have authentication that can be 

572 provided in one of multiple optional ways (for example, with OAuth2 

573 or in a cookie). 

574 """ 

575 ), 

576 ] = True, 

577 ): 

578 if not scopes: 1abcdef

579 scopes = {} 1abcdef

580 flows = OAuthFlowsModel( 1abcdef

581 authorizationCode=cast( 

582 Any, 

583 { 

584 "authorizationUrl": authorizationUrl, 

585 "tokenUrl": tokenUrl, 

586 "refreshUrl": refreshUrl, 

587 "scopes": scopes, 

588 }, 

589 ) 

590 ) 

591 super().__init__( 1abcdef

592 flows=flows, 

593 scheme_name=scheme_name, 

594 description=description, 

595 auto_error=auto_error, 

596 ) 

597 

598 async def __call__(self, request: Request) -> Optional[str]: 1abcdef

599 authorization = request.headers.get("Authorization") 2ccdc[cecfc]cgchc^cicjc_ckclc`cmcnc{cocpc|cqcrc}csctc~cucvcadwcxcbdyczccd

600 scheme, param = get_authorization_scheme_param(authorization) 2ccdc[cecfc]cgchc^cicjc_ckclc`cmcnc{cocpc|cqcrc}csctc~cucvcadwcxcbdyczccd

601 if not authorization or scheme.lower() != "bearer": 2ccdc[cecfc]cgchc^cicjc_ckclc`cmcnc{cocpc|cqcrc}csctc~cucvcadwcxcbdyczccd

602 if self.auto_error: 2ccdcecfcgchcicjckclcmcncocpcqcrcsctcucvcwcxcyczc

603 raise HTTPException( 2ccdcecfcgchcicjckclcmcncocpcqcrcsctcucvcwcxcyczc

604 status_code=HTTP_401_UNAUTHORIZED, 

605 detail="Not authenticated", 

606 headers={"WWW-Authenticate": "Bearer"}, 

607 ) 

608 else: 

609 return None # pragma: nocover 

610 return param 2[c]c^c_c`c{c|c}c~cadbdcd

611 

612 

613class SecurityScopes: 1abcdef

614 """ 

615 This is a special class that you can define in a parameter in a dependency to 

616 obtain the OAuth2 scopes required by all the dependencies in the same chain. 

617 

618 This way, multiple dependencies can have different scopes, even when used in the 

619 same *path operation*. And with this, you can access all the scopes required in 

620 all those dependencies in a single place. 

621 

622 Read more about it in the 

623 [FastAPI docs for OAuth2 scopes](https://fastapi.tiangolo.com/advanced/security/oauth2-scopes/). 

624 """ 

625 

626 def __init__( 1abcdef

627 self, 

628 scopes: Annotated[ 

629 Optional[List[str]], 

630 Doc( 

631 """ 

632 This will be filled by FastAPI. 

633 """ 

634 ), 

635 ] = None, 

636 ): 

637 self.scopes: Annotated[ 2NdOdPdbbg h i j k cbdbebQdRdSdfbl m n o p gbhbibTdUdVdjbq r s t u kblbmbWdXdYdnbv w x y z obpbqbZd0d1drbA B C D E sbtbub2d3d4dvbF G H I J wbxbyb

638 List[str], 

639 Doc( 

640 """ 

641 The list of all the scopes required by dependencies. 

642 """ 

643 ), 

644 ] = scopes or [] 

645 self.scope_str: Annotated[ 2NdOdPdbbg h i j k cbdbebQdRdSdfbl m n o p gbhbibTdUdVdjbq r s t u kblbmbWdXdYdnbv w x y z obpbqbZd0d1drbA B C D E sbtbub2d3d4dvbF G H I J wbxbyb

646 str, 

647 Doc( 

648 """ 

649 All the scopes required by all the dependencies in a single string 

650 separated by spaces, as defined in the OAuth2 specification. 

651 """ 

652 ), 

653 ] = " ".join(self.scopes)