or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-responses.mdapi-routing.mdbackground-tasks.mdcore-application.mddata-utilities.mddependency-injection.mdexception-handling.mdindex.mdmiddleware.mdrequest-parameters.mdrequest-response.mdsecurity-authentication.mdstatic-templating.mdtesting.mdwebsocket-support.md

security-authentication.mddocs/

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

```