0
# Security & Authentication
1
2
FastAPI provides comprehensive security and authentication components that integrate seamlessly with dependency injection and automatic OpenAPI documentation generation. These components support various authentication schemes including API keys, HTTP authentication, and OAuth2.
3
4
## Capabilities
5
6
### Security Base Function
7
8
Core function for declaring security dependencies with optional scopes for fine-grained permission control.
9
10
```python { .api }
11
def Security(
12
dependency: Callable = None,
13
*,
14
scopes: List[str] = None,
15
use_cache: bool = True
16
) -> Any:
17
"""
18
Declare security dependencies with scopes.
19
20
Parameters:
21
- dependency: Security dependency callable
22
- scopes: List of required security scopes
23
- use_cache: Whether to cache dependency results
24
25
Returns:
26
Security dependency with scope validation
27
"""
28
```
29
30
### Security Scopes Class
31
32
Class for handling and validating security scopes in authentication systems.
33
34
```python { .api }
35
class SecurityScopes:
36
def __init__(self, scopes: List[str] = None) -> None:
37
"""
38
Security scopes container.
39
40
Parameters:
41
- scopes: List of security scopes
42
"""
43
self.scopes = scopes or []
44
self.scope_str = " ".join(self.scopes)
45
```
46
47
### API Key Authentication
48
49
Classes for implementing API key authentication via different transport mechanisms.
50
51
```python { .api }
52
class APIKeyQuery:
53
def __init__(
54
self,
55
*,
56
name: str,
57
scheme_name: str = None,
58
description: str = None,
59
auto_error: bool = True
60
) -> None:
61
"""
62
API key authentication via query parameters.
63
64
Parameters:
65
- name: Query parameter name for the API key
66
- scheme_name: Security scheme name for OpenAPI
67
- description: Security scheme description
68
- auto_error: Automatically raise HTTPException on authentication failure
69
"""
70
71
async def __call__(self, request: Request) -> str:
72
"""Extract and validate API key from query parameters."""
73
74
class APIKeyHeader:
75
def __init__(
76
self,
77
*,
78
name: str,
79
scheme_name: str = None,
80
description: str = None,
81
auto_error: bool = True
82
) -> None:
83
"""
84
API key authentication via headers.
85
86
Parameters:
87
- name: Header name for the API key
88
- scheme_name: Security scheme name for OpenAPI
89
- description: Security scheme description
90
- auto_error: Automatically raise HTTPException on authentication failure
91
"""
92
93
async def __call__(self, request: Request) -> str:
94
"""Extract and validate API key from headers."""
95
96
class APIKeyCookie:
97
def __init__(
98
self,
99
*,
100
name: str,
101
scheme_name: str = None,
102
description: str = None,
103
auto_error: bool = True
104
) -> None:
105
"""
106
API key authentication via cookies.
107
108
Parameters:
109
- name: Cookie name for the API key
110
- scheme_name: Security scheme name for OpenAPI
111
- description: Security scheme description
112
- auto_error: Automatically raise HTTPException on authentication failure
113
"""
114
115
async def __call__(self, request: Request) -> str:
116
"""Extract and validate API key from cookies."""
117
```
118
119
### HTTP Authentication
120
121
Classes for implementing standard HTTP authentication schemes.
122
123
```python { .api }
124
class HTTPBasic:
125
def __init__(
126
self,
127
*,
128
scheme_name: str = None,
129
realm: str = None,
130
description: str = None,
131
auto_error: bool = True
132
) -> None:
133
"""
134
HTTP Basic authentication.
135
136
Parameters:
137
- scheme_name: Security scheme name for OpenAPI
138
- realm: Authentication realm
139
- description: Security scheme description
140
- auto_error: Automatically raise HTTPException on authentication failure
141
"""
142
143
async def __call__(self, request: Request) -> HTTPBasicCredentials:
144
"""Extract and validate Basic authentication credentials."""
145
146
class HTTPBasicCredentials:
147
def __init__(self, username: str, password: str) -> None:
148
"""
149
HTTP Basic authentication credentials.
150
151
Parameters:
152
- username: Username from Basic auth
153
- password: Password from Basic auth
154
"""
155
self.username = username
156
self.password = password
157
158
class HTTPBearer:
159
def __init__(
160
self,
161
*,
162
bearerFormat: str = None,
163
scheme_name: str = None,
164
description: str = None,
165
auto_error: bool = True
166
) -> None:
167
"""
168
HTTP Bearer token authentication.
169
170
Parameters:
171
- bearerFormat: Bearer token format (e.g., "JWT")
172
- scheme_name: Security scheme name for OpenAPI
173
- description: Security scheme description
174
- auto_error: Automatically raise HTTPException on authentication failure
175
"""
176
177
async def __call__(self, request: Request) -> HTTPAuthorizationCredentials:
178
"""Extract and validate Bearer token credentials."""
179
180
class HTTPAuthorizationCredentials:
181
def __init__(self, scheme: str, credentials: str) -> None:
182
"""
183
HTTP authorization credentials.
184
185
Parameters:
186
- scheme: Authorization scheme (e.g., "Bearer")
187
- credentials: Authorization credentials (e.g., token)
188
"""
189
self.scheme = scheme
190
self.credentials = credentials
191
192
class HTTPDigest:
193
def __init__(
194
self,
195
*,
196
scheme_name: str = None,
197
realm: str = None,
198
description: str = None,
199
auto_error: bool = True
200
) -> None:
201
"""
202
HTTP Digest authentication.
203
204
Parameters:
205
- scheme_name: Security scheme name for OpenAPI
206
- realm: Authentication realm
207
- description: Security scheme description
208
- auto_error: Automatically raise HTTPException on authentication failure
209
"""
210
211
async def __call__(self, request: Request) -> HTTPAuthorizationCredentials:
212
"""Extract and validate Digest authentication credentials."""
213
```
214
215
### OAuth2 Authentication
216
217
Classes for implementing OAuth2 authentication flows.
218
219
```python { .api }
220
class OAuth2:
221
def __init__(
222
self,
223
*,
224
flows: Dict[str, Dict[str, Any]] = None,
225
scheme_name: str = None,
226
description: str = None,
227
auto_error: bool = True
228
) -> None:
229
"""
230
OAuth2 authentication base class.
231
232
Parameters:
233
- flows: OAuth2 flows configuration
234
- scheme_name: Security scheme name for OpenAPI
235
- description: Security scheme description
236
- auto_error: Automatically raise HTTPException on authentication failure
237
"""
238
239
class OAuth2PasswordBearer:
240
def __init__(
241
self,
242
tokenUrl: str,
243
*,
244
scheme_name: str = None,
245
scopes: Dict[str, str] = None,
246
description: str = None,
247
auto_error: bool = True
248
) -> None:
249
"""
250
OAuth2 password bearer authentication.
251
252
Parameters:
253
- tokenUrl: URL for token endpoint
254
- scheme_name: Security scheme name for OpenAPI
255
- scopes: Available OAuth2 scopes
256
- description: Security scheme description
257
- auto_error: Automatically raise HTTPException on authentication failure
258
"""
259
260
async def __call__(self, request: Request) -> str:
261
"""Extract and validate OAuth2 bearer token."""
262
263
class OAuth2AuthorizationCodeBearer:
264
def __init__(
265
self,
266
authorizationUrl: str,
267
tokenUrl: str,
268
*,
269
refreshUrl: str = None,
270
scheme_name: str = None,
271
scopes: Dict[str, str] = None,
272
description: str = None,
273
auto_error: bool = True
274
) -> None:
275
"""
276
OAuth2 authorization code bearer authentication.
277
278
Parameters:
279
- authorizationUrl: URL for authorization endpoint
280
- tokenUrl: URL for token endpoint
281
- refreshUrl: URL for token refresh endpoint
282
- scheme_name: Security scheme name for OpenAPI
283
- scopes: Available OAuth2 scopes
284
- description: Security scheme description
285
- auto_error: Automatically raise HTTPException on authentication failure
286
"""
287
288
async def __call__(self, request: Request) -> str:
289
"""Extract and validate OAuth2 authorization code bearer token."""
290
291
class OAuth2PasswordRequestForm:
292
def __init__(
293
self,
294
*,
295
grant_type: str = Form(regex="password"),
296
username: str = Form(),
297
password: str = Form(),
298
scope: str = Form(""),
299
client_id: str = Form(None),
300
client_secret: str = Form(None)
301
) -> None:
302
"""
303
OAuth2 password request form.
304
305
Parameters:
306
- grant_type: OAuth2 grant type (must be "password")
307
- username: User username
308
- password: User password
309
- scope: Requested scopes
310
- client_id: OAuth2 client ID
311
- client_secret: OAuth2 client secret
312
"""
313
314
class OAuth2PasswordRequestFormStrict:
315
def __init__(
316
self,
317
*,
318
grant_type: str = Form(regex="password"),
319
username: str = Form(),
320
password: str = Form(),
321
scope: str = Form(""),
322
client_id: str = Form(),
323
client_secret: str = Form()
324
) -> None:
325
"""
326
Strict OAuth2 password request form with required client credentials.
327
328
Parameters:
329
- grant_type: OAuth2 grant type (must be "password")
330
- username: User username
331
- password: User password
332
- scope: Requested scopes
333
- client_id: OAuth2 client ID (required)
334
- client_secret: OAuth2 client secret (required)
335
"""
336
```
337
338
### OpenID Connect Authentication
339
340
Class for implementing OpenID Connect authentication.
341
342
```python { .api }
343
class OpenIdConnect:
344
def __init__(
345
self,
346
*,
347
openIdConnectUrl: str,
348
scheme_name: str = None,
349
description: str = None,
350
auto_error: bool = True
351
) -> None:
352
"""
353
OpenID Connect authentication.
354
355
Parameters:
356
- openIdConnectUrl: OpenID Connect discovery URL
357
- scheme_name: Security scheme name for OpenAPI
358
- description: Security scheme description
359
- auto_error: Automatically raise HTTPException on authentication failure
360
"""
361
362
async def __call__(self, request: Request) -> str:
363
"""Extract and validate OpenID Connect token."""
364
```
365
366
## Usage Examples
367
368
### API Key Authentication
369
370
```python
371
from fastapi import FastAPI, Depends, HTTPException, status
372
from fastapi.security import APIKeyHeader
373
374
app = FastAPI()
375
376
API_KEY = "your-secret-api-key"
377
api_key_header = APIKeyHeader(name="X-API-Key")
378
379
def verify_api_key(api_key: str = Depends(api_key_header)):
380
if api_key != API_KEY:
381
raise HTTPException(
382
status_code=status.HTTP_401_UNAUTHORIZED,
383
detail="Invalid API Key"
384
)
385
return api_key
386
387
@app.get("/protected")
388
def protected_route(api_key: str = Depends(verify_api_key)):
389
return {"message": "This is a protected route", "api_key": api_key}
390
```
391
392
### HTTP Basic Authentication
393
394
```python
395
import secrets
396
from fastapi import FastAPI, Depends, HTTPException, status
397
from fastapi.security import HTTPBasic, HTTPBasicCredentials
398
399
app = FastAPI()
400
401
security = HTTPBasic()
402
403
def get_current_username(credentials: HTTPBasicCredentials = Depends(security)):
404
current_username_bytes = credentials.username.encode("utf8")
405
correct_username_bytes = b"testuser"
406
is_correct_username = secrets.compare_digest(
407
current_username_bytes, correct_username_bytes
408
)
409
current_password_bytes = credentials.password.encode("utf8")
410
correct_password_bytes = b"testpass"
411
is_correct_password = secrets.compare_digest(
412
current_password_bytes, correct_password_bytes
413
)
414
if not (is_correct_username and is_correct_password):
415
raise HTTPException(
416
status_code=status.HTTP_401_UNAUTHORIZED,
417
detail="Incorrect username or password",
418
headers={"WWW-Authenticate": "Basic"},
419
)
420
return credentials.username
421
422
@app.get("/users/me")
423
def read_current_user(username: str = Depends(get_current_username)):
424
return {"username": username}
425
```
426
427
### HTTP Bearer Token Authentication
428
429
```python
430
from fastapi import FastAPI, Depends, HTTPException, status
431
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
432
433
app = FastAPI()
434
435
security = HTTPBearer()
436
437
def verify_token(credentials: HTTPAuthorizationCredentials = Depends(security)):
438
token = credentials.credentials
439
if token != "valid-bearer-token":
440
raise HTTPException(
441
status_code=status.HTTP_401_UNAUTHORIZED,
442
detail="Invalid authentication token"
443
)
444
return token
445
446
@app.get("/protected")
447
def protected_route(token: str = Depends(verify_token)):
448
return {"message": "Access granted", "token": token}
449
```
450
451
### OAuth2 Password Bearer Authentication
452
453
```python
454
from datetime import datetime, timedelta
455
from jose import JWTError, jwt
456
from passlib.context import CryptContext
457
from fastapi import FastAPI, Depends, HTTPException, status
458
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
459
460
app = FastAPI()
461
462
SECRET_KEY = "your-secret-key"
463
ALGORITHM = "HS256"
464
ACCESS_TOKEN_EXPIRE_MINUTES = 30
465
466
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
467
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
468
469
fake_users_db = {
470
"testuser": {
471
"username": "testuser",
472
"hashed_password": "$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW",
473
"email": "test@example.com",
474
}
475
}
476
477
def verify_password(plain_password, hashed_password):
478
return pwd_context.verify(plain_password, hashed_password)
479
480
def get_password_hash(password):
481
return pwd_context.hash(password)
482
483
def get_user(db, username: str):
484
if username in db:
485
user_dict = db[username]
486
return user_dict
487
488
def authenticate_user(fake_db, username: str, password: str):
489
user = get_user(fake_db, username)
490
if not user:
491
return False
492
if not verify_password(password, user["hashed_password"]):
493
return False
494
return user
495
496
def create_access_token(data: dict, expires_delta: timedelta = None):
497
to_encode = data.copy()
498
if expires_delta:
499
expire = datetime.utcnow() + expires_delta
500
else:
501
expire = datetime.utcnow() + timedelta(minutes=15)
502
to_encode.update({"exp": expire})
503
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
504
return encoded_jwt
505
506
async def get_current_user(token: str = Depends(oauth2_scheme)):
507
credentials_exception = HTTPException(
508
status_code=status.HTTP_401_UNAUTHORIZED,
509
detail="Could not validate credentials",
510
headers={"WWW-Authenticate": "Bearer"},
511
)
512
try:
513
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
514
username: str = payload.get("sub")
515
if username is None:
516
raise credentials_exception
517
except JWTError:
518
raise credentials_exception
519
user = get_user(fake_users_db, username=username)
520
if user is None:
521
raise credentials_exception
522
return user
523
524
@app.post("/token")
525
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
526
user = authenticate_user(fake_users_db, form_data.username, form_data.password)
527
if not user:
528
raise HTTPException(
529
status_code=status.HTTP_401_UNAUTHORIZED,
530
detail="Incorrect username or password",
531
headers={"WWW-Authenticate": "Bearer"},
532
)
533
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
534
access_token = create_access_token(
535
data={"sub": user["username"]}, expires_delta=access_token_expires
536
)
537
return {"access_token": access_token, "token_type": "bearer"}
538
539
@app.get("/users/me")
540
async def read_users_me(current_user: dict = Depends(get_current_user)):
541
return current_user
542
```
543
544
### Security with Scopes
545
546
```python
547
from fastapi import FastAPI, Depends, HTTPException, status
548
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm, SecurityScopes
549
550
app = FastAPI()
551
552
oauth2_scheme = OAuth2PasswordBearer(
553
tokenUrl="token",
554
scopes={
555
"read": "Read access",
556
"write": "Write access",
557
"admin": "Admin access"
558
}
559
)
560
561
def get_current_user(
562
security_scopes: SecurityScopes,
563
token: str = Depends(oauth2_scheme)
564
):
565
if security_scopes.scopes:
566
authenticate_value = f'Bearer scope="{security_scopes.scope_str}"'
567
else:
568
authenticate_value = "Bearer"
569
570
credentials_exception = HTTPException(
571
status_code=status.HTTP_401_UNAUTHORIZED,
572
detail="Could not validate credentials",
573
headers={"WWW-Authenticate": authenticate_value},
574
)
575
576
# Token validation logic here
577
# For demo purposes, assume token is valid and contains scopes
578
token_scopes = ["read", "write"] # Scopes from decoded token
579
580
for scope in security_scopes.scopes:
581
if scope not in token_scopes:
582
raise HTTPException(
583
status_code=status.HTTP_401_UNAUTHORIZED,
584
detail="Not enough permissions",
585
headers={"WWW-Authenticate": authenticate_value},
586
)
587
588
return {"username": "testuser", "scopes": token_scopes}
589
590
@app.get("/read-data")
591
async def read_data(
592
current_user: dict = Security(get_current_user, scopes=["read"])
593
):
594
return {"data": "This requires read access"}
595
596
@app.post("/write-data")
597
async def write_data(
598
current_user: dict = Security(get_current_user, scopes=["write"])
599
):
600
return {"message": "Data written successfully"}
601
602
@app.delete("/admin-action")
603
async def admin_action(
604
current_user: dict = Security(get_current_user, scopes=["admin"])
605
):
606
return {"message": "Admin action performed"}
607
```
608
609
### Multiple Authentication Methods
610
611
```python
612
from fastapi import FastAPI, Depends, HTTPException, status
613
from fastapi.security import HTTPBearer, APIKeyHeader
614
from typing import Union
615
616
app = FastAPI()
617
618
bearer_scheme = HTTPBearer(auto_error=False)
619
api_key_scheme = APIKeyHeader(name="X-API-Key", auto_error=False)
620
621
async def get_current_user(
622
bearer_token: str = Depends(bearer_scheme),
623
api_key: str = Depends(api_key_scheme)
624
) -> dict:
625
# Try bearer token first
626
if bearer_token:
627
if bearer_token.credentials == "valid-bearer-token":
628
return {"username": "bearer_user", "auth_method": "bearer"}
629
630
# Try API key second
631
if api_key:
632
if api_key == "valid-api-key":
633
return {"username": "api_user", "auth_method": "api_key"}
634
635
# Neither authentication method worked
636
raise HTTPException(
637
status_code=status.HTTP_401_UNAUTHORIZED,
638
detail="Invalid authentication credentials"
639
)
640
641
@app.get("/protected")
642
async def protected_route(current_user: dict = Depends(get_current_user)):
643
return {
644
"message": f"Hello {current_user['username']}",
645
"auth_method": current_user["auth_method"]
646
}
647
```
648
649
### Custom Security Dependency
650
651
```python
652
from fastapi import FastAPI, Request, HTTPException, Depends, status
653
654
app = FastAPI()
655
656
class CustomAuth:
657
def __init__(self, required_role: str = None):
658
self.required_role = required_role
659
660
async def __call__(self, request: Request):
661
# Custom authentication logic
662
auth_header = request.headers.get("Authorization")
663
if not auth_header:
664
raise HTTPException(
665
status_code=status.HTTP_401_UNAUTHORIZED,
666
detail="Authorization header required"
667
)
668
669
# Validate custom token format
670
if not auth_header.startswith("Custom "):
671
raise HTTPException(
672
status_code=status.HTTP_401_UNAUTHORIZED,
673
detail="Invalid token format"
674
)
675
676
token = auth_header.replace("Custom ", "")
677
678
# Mock user validation
679
if token == "valid-custom-token":
680
user = {"username": "custom_user", "role": "admin"}
681
else:
682
raise HTTPException(
683
status_code=status.HTTP_401_UNAUTHORIZED,
684
detail="Invalid token"
685
)
686
687
# Check role if required
688
if self.required_role and user.get("role") != self.required_role:
689
raise HTTPException(
690
status_code=status.HTTP_403_FORBIDDEN,
691
detail="Insufficient permissions"
692
)
693
694
return user
695
696
# Use custom security
697
auth = CustomAuth()
698
admin_auth = CustomAuth(required_role="admin")
699
700
@app.get("/user-info")
701
async def get_user_info(user: dict = Depends(auth)):
702
return user
703
704
@app.get("/admin-only")
705
async def admin_only(user: dict = Depends(admin_auth)):
706
return {"message": "Admin access granted", "user": user}
707
```