or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

common-utilities.mddevice-flow.mderror-handling.mdindex.mdoauth1.mdoauth2-clients.mdoauth2-servers.mdopenid-connect.mdrequest-validation.mdtoken-management.md

oauth2-servers.mddocs/

0

# OAuth 2.0 Servers

1

2

Complete server-side OAuth 2.0 implementation providing authorization servers, resource servers, and token endpoints. Supports all standard grant types, token introspection, revocation, and comprehensive request validation.

3

4

## Capabilities

5

6

### Authorization Endpoint

7

8

OAuth 2.0 authorization endpoint handling authorization requests and user consent. Manages the authorization code flow by presenting authorization pages to users and issuing authorization codes.

9

10

```python { .api }

11

class AuthorizationEndpoint:

12

def __init__(self, default_response_type: str, default_token_type: str, response_types: dict[str, callable]): ...

13

14

def create_authorization_response(

15

self,

16

uri: str,

17

http_method: str = "GET",

18

body: str | None = None,

19

headers: dict[str, str] | None = None,

20

scopes: list[str] | None = None,

21

credentials: dict[str, str] | None = None,

22

) -> tuple[dict[str, str], str, int]:

23

"""

24

Create authorization response.

25

26

Parameters:

27

- uri: Authorization request URI

28

- http_method: HTTP method

29

- body: Request body

30

- headers: Request headers

31

- scopes: Requested scopes

32

- credentials: User credentials and consent info

33

34

Returns:

35

Tuple of (headers, body, status_code) for authorization response

36

"""

37

38

def validate_authorization_request(

39

self,

40

uri: str,

41

http_method: str = "GET",

42

body: str | None = None,

43

headers: dict[str, str] | None = None,

44

) -> None:

45

"""Validate authorization request parameters."""

46

```

47

48

### Token Endpoint

49

50

OAuth 2.0 token endpoint handling token requests for all grant types. Issues access tokens, refresh tokens, and manages token exchange operations.

51

52

```python { .api }

53

class TokenEndpoint:

54

def __init__(

55

self,

56

default_grant_type=None,

57

default_token_type=None,

58

token_generator=None,

59

refresh_token_generator=None,

60

expires_in=None,

61

): ...

62

63

def create_token_response(

64

self,

65

uri: str,

66

http_method: str = "POST",

67

body: str | None = None,

68

headers: dict[str, str] | None = None,

69

credentials: dict[str, str] | None = None,

70

grant_type_handler=None,

71

**kwargs,

72

) -> tuple[dict[str, str], str, int]:

73

"""

74

Create token response for any grant type.

75

76

Parameters:

77

- uri: Token request URI

78

- http_method: HTTP method

79

- body: Request body

80

- headers: Request headers

81

- credentials: Client credentials

82

- grant_type_handler: Custom grant type handler

83

84

Returns:

85

Tuple of (headers, body, status_code) for token response

86

"""

87

88

def validate_token_request(self, request) -> None:

89

"""Validate token request parameters."""

90

```

91

92

### Resource Endpoint

93

94

OAuth 2.0 resource endpoint for validating access tokens when accessing protected resources. Verifies token validity, scope, and expiration.

95

96

```python { .api }

97

class ResourceEndpoint:

98

def __init__(self, default_token=None, token_generator=None, expires_in=None): ...

99

100

def validate_protected_resource_request(

101

self,

102

uri: str,

103

http_method: str = "GET",

104

body: str | None = None,

105

headers: dict[str, str] | None = None,

106

scopes: list[str] | None = None,

107

) -> tuple[bool, dict]:

108

"""

109

Validate protected resource request.

110

111

Parameters:

112

- uri: Resource request URI

113

- http_method: HTTP method

114

- body: Request body

115

- headers: Request headers

116

- scopes: Required scopes

117

118

Returns:

119

Tuple of (valid, request) where valid indicates if token is valid

120

"""

121

```

122

123

### Revocation Endpoint

124

125

OAuth 2.0 token revocation endpoint (RFC 7009) for invalidating access and refresh tokens. Allows clients to revoke tokens when they're no longer needed.

126

127

```python { .api }

128

class RevocationEndpoint:

129

def __init__(self, request_validator, supported_token_types=None, enable_jsonp=False): ...

130

131

def create_revocation_response(

132

self,

133

uri: str,

134

http_method: str = "POST",

135

body: str | None = None,

136

headers: dict[str, str] | None = None,

137

) -> tuple[dict[str, str], str, int]:

138

"""

139

Create token revocation response.

140

141

Parameters:

142

- uri: Revocation request URI

143

- http_method: HTTP method

144

- body: Request body containing token to revoke

145

- headers: Request headers

146

147

Returns:

148

Tuple of (headers, body, status_code) for revocation response

149

"""

150

151

def validate_revocation_request(self, request) -> None:

152

"""Validate token revocation request."""

153

```

154

155

### Introspection Endpoint

156

157

OAuth 2.0 token introspection endpoint (RFC 7662) for checking token status and metadata. Allows resource servers to query token information.

158

159

```python { .api }

160

class IntrospectEndpoint:

161

def __init__(self, request_validator, supported_token_types=None): ...

162

163

def create_introspect_response(

164

self,

165

uri: str,

166

http_method: str = "POST",

167

body: str | None = None,

168

headers: dict[str, str] | None = None,

169

) -> tuple[dict[str, str], str, int]:

170

"""

171

Create token introspection response.

172

173

Parameters:

174

- uri: Introspection request URI

175

- http_method: HTTP method

176

- body: Request body containing token to introspect

177

- headers: Request headers

178

179

Returns:

180

Tuple of (headers, body, status_code) with token metadata

181

"""

182

183

def validate_introspect_request(self, request) -> None:

184

"""Validate token introspection request."""

185

```

186

187

### Metadata Endpoint

188

189

OAuth 2.0 authorization server metadata endpoint (RFC 8414) for publishing server capabilities and configuration. Enables automatic client configuration.

190

191

```python { .api }

192

class MetadataEndpoint:

193

def __init__(self, endpoints, claims=None, **kwargs): ...

194

195

def create_metadata_response(

196

self,

197

uri: str,

198

http_method: str = "GET",

199

body: str | None = None,

200

headers: dict[str, str] | None = None,

201

) -> tuple[dict[str, str], str, int]:

202

"""

203

Create authorization server metadata response.

204

205

Returns:

206

Tuple of (headers, body, status_code) with server metadata JSON

207

"""

208

```

209

210

### Pre-configured Servers

211

212

Complete OAuth 2.0 server implementations combining multiple endpoints for common deployment scenarios.

213

214

#### Web Application Server

215

216

```python { .api }

217

class WebApplicationServer:

218

def __init__(

219

self,

220

request_validator,

221

token_expires_in=None,

222

token_generator=None,

223

refresh_token_generator=None,

224

**kwargs,

225

):

226

"""

227

Complete OAuth 2.0 server for web applications.

228

229

Combines AuthorizationEndpoint, TokenEndpoint, and ResourceEndpoint

230

to support the authorization code grant flow.

231

"""

232

233

# Inherits methods from AuthorizationEndpoint, TokenEndpoint, ResourceEndPoint

234

def create_authorization_response(self, uri, http_method="GET", body=None, headers=None, scopes=None, credentials=None): ...

235

def create_token_response(self, uri, http_method="POST", body=None, headers=None, credentials=None, **kwargs): ...

236

def validate_protected_resource_request(self, uri, http_method="GET", body=None, headers=None, scopes=None): ...

237

```

238

239

#### Mobile Application Server

240

241

```python { .api }

242

class MobileApplicationServer:

243

def __init__(

244

self,

245

request_validator,

246

token_expires_in=None,

247

token_generator=None,

248

**kwargs,

249

):

250

"""

251

OAuth 2.0 server for mobile applications.

252

253

Supports the implicit grant flow for public clients

254

that cannot securely store credentials.

255

"""

256

```

257

258

#### Legacy Application Server

259

260

```python { .api }

261

class LegacyApplicationServer:

262

def __init__(

263

self,

264

request_validator,

265

token_expires_in=None,

266

token_generator=None,

267

refresh_token_generator=None,

268

**kwargs,

269

):

270

"""

271

OAuth 2.0 server for legacy applications.

272

273

Supports the password credentials grant flow for

274

trusted first-party applications.

275

"""

276

```

277

278

#### Backend Application Server

279

280

```python { .api }

281

class BackendApplicationServer:

282

def __init__(

283

self,

284

request_validator,

285

token_expires_in=None,

286

token_generator=None,

287

**kwargs,

288

):

289

"""

290

OAuth 2.0 server for backend applications.

291

292

Supports the client credentials grant flow for

293

machine-to-machine authentication.

294

"""

295

```

296

297

#### Base Server

298

299

```python { .api }

300

class Server:

301

def __init__(

302

self,

303

request_validator,

304

token_expires_in=None,

305

token_generator=None,

306

refresh_token_generator=None,

307

**kwargs,

308

):

309

"""

310

Base OAuth 2.0 server supporting all grant types.

311

312

Provides a flexible foundation for custom server implementations

313

with support for authorization code, implicit, password credentials,

314

and client credentials grants.

315

"""

316

```

317

318

## Usage Examples

319

320

### Authorization Code Flow Server

321

322

```python

323

from oauthlib.oauth2 import WebApplicationServer, RequestValidator

324

from oauthlib.common import generate_token

325

326

class MyRequestValidator(RequestValidator):

327

def validate_client_id(self, client_id, request, *args, **kwargs):

328

# Check if client_id exists in your database

329

return client_id in ['your-client-id', 'another-client']

330

331

def authenticate_client(self, request, *args, **kwargs):

332

# Authenticate client credentials

333

client_id = getattr(request, 'client_id', None)

334

client_secret = getattr(request, 'client_secret', None)

335

return verify_client_credentials(client_id, client_secret)

336

337

def validate_redirect_uri(self, client_id, redirect_uri, request, *args, **kwargs):

338

# Validate redirect URI is registered for this client

339

return redirect_uri in get_registered_redirect_uris(client_id)

340

341

def get_default_scopes(self, client_id, request, *args, **kwargs):

342

# Return default scopes for client

343

return ['read']

344

345

def validate_scopes(self, client_id, scopes, client, request, *args, **kwargs):

346

# Validate requested scopes

347

allowed_scopes = get_client_scopes(client_id)

348

return all(scope in allowed_scopes for scope in scopes)

349

350

def save_authorization_code(self, client_id, code, request, *args, **kwargs):

351

# Store authorization code in database

352

store_authorization_code(client_id, code, request)

353

354

def validate_code(self, client_id, code, client, request, *args, **kwargs):

355

# Validate authorization code

356

return is_valid_authorization_code(client_id, code)

357

358

def confirm_redirect_uri(self, client_id, code, redirect_uri, client, request, *args, **kwargs):

359

# Confirm redirect URI matches original request

360

return get_code_redirect_uri(code) == redirect_uri

361

362

def save_token(self, token, request, *args, **kwargs):

363

# Store access token in database

364

store_access_token(token, request)

365

366

def validate_bearer_token(self, token, scopes, request):

367

# Validate bearer token and scopes

368

return is_valid_bearer_token(token, scopes)

369

370

def get_default_redirect_uri(self, client_id, request, *args, **kwargs):

371

# Return default redirect URI for client

372

return get_client_default_redirect_uri(client_id)

373

374

# Create server

375

validator = MyRequestValidator()

376

server = WebApplicationServer(validator)

377

378

# Handle authorization request

379

def handle_authorization(request):

380

try:

381

# Extract request details

382

uri = request.url

383

http_method = request.method

384

body = request.body

385

headers = dict(request.headers)

386

387

# Check if user is authenticated and consented

388

if not user_authenticated(request):

389

return redirect_to_login()

390

391

if not user_consented(request):

392

return show_consent_form()

393

394

# Create authorization response

395

headers, body, status = server.create_authorization_response(

396

uri, http_method, body, headers,

397

scopes=request.args.get('scope', '').split(),

398

credentials={'user_id': get_user_id(request)}

399

)

400

401

return Response(body, status=status, headers=headers)

402

403

except OAuth2Error as e:

404

return Response(e.json, status=e.status_code,

405

headers={'Content-Type': 'application/json'})

406

407

# Handle token request

408

def handle_token(request):

409

try:

410

uri = request.url

411

http_method = request.method

412

body = request.body

413

headers = dict(request.headers)

414

415

headers, body, status = server.create_token_response(

416

uri, http_method, body, headers

417

)

418

419

return Response(body, status=status, headers=headers)

420

421

except OAuth2Error as e:

422

return Response(e.json, status=e.status_code,

423

headers={'Content-Type': 'application/json'})

424

```

425

426

### Protected Resource Validation

427

428

```python

429

from oauthlib.oauth2 import ResourceEndpoint

430

from functools import wraps

431

432

# Create resource endpoint

433

resource_endpoint = ResourceEndpoint()

434

435

def require_oauth(*required_scopes):

436

"""Decorator to protect API endpoints with OAuth."""

437

def decorator(f):

438

@wraps(f)

439

def decorated_function(*args, **kwargs):

440

# Extract request details

441

uri = request.url

442

http_method = request.method

443

body = request.body

444

headers = dict(request.headers)

445

446

try:

447

# Validate token and scopes

448

valid, oauth_request = resource_endpoint.validate_protected_resource_request(

449

uri, http_method, body, headers, required_scopes

450

)

451

452

if not valid:

453

return Response('Unauthorized', status=401)

454

455

# Add OAuth info to request context

456

request.oauth = oauth_request

457

458

return f(*args, **kwargs)

459

460

except OAuth2Error as e:

461

return Response(e.json, status=e.status_code,

462

headers={'Content-Type': 'application/json'})

463

464

return decorated_function

465

return decorator

466

467

# Usage in API endpoints

468

@app.route('/api/user/profile')

469

@require_oauth('profile:read')

470

def get_user_profile():

471

user_id = request.oauth.user_id

472

return jsonify(get_user_data(user_id))

473

474

@app.route('/api/user/settings', methods=['POST'])

475

@require_oauth('profile:write')

476

def update_user_settings():

477

user_id = request.oauth.user_id

478

update_user_data(user_id, request.json)

479

return jsonify({'status': 'updated'})

480

```

481

482

### Token Introspection

483

484

```python

485

from oauthlib.oauth2 import IntrospectEndpoint

486

487

# Create introspection endpoint

488

introspect_endpoint = IntrospectEndpoint(validator)

489

490

def handle_introspect(request):

491

"""Handle token introspection requests from resource servers."""

492

try:

493

uri = request.url

494

http_method = request.method

495

body = request.body

496

headers = dict(request.headers)

497

498

headers, body, status = introspect_endpoint.create_introspect_response(

499

uri, http_method, body, headers

500

)

501

502

return Response(body, status=status, headers=headers)

503

504

except OAuth2Error as e:

505

return Response(e.json, status=e.status_code,

506

headers={'Content-Type': 'application/json'})

507

```

508

509

### Authorization Server Metadata

510

511

```python

512

from oauthlib.oauth2 import MetadataEndpoint

513

514

# Create metadata endpoint

515

metadata = MetadataEndpoint([

516

('authorization_endpoint', 'https://auth.example.com/authorize'),

517

('token_endpoint', 'https://auth.example.com/token'),

518

('revocation_endpoint', 'https://auth.example.com/revoke'),

519

('introspection_endpoint', 'https://auth.example.com/introspect'),

520

], claims=[

521

('issuer', 'https://auth.example.com'),

522

('response_types_supported', ['code', 'token']),

523

('grant_types_supported', ['authorization_code', 'implicit', 'client_credentials']),

524

('token_endpoint_auth_methods_supported', ['client_secret_basic', 'client_secret_post']),

525

('scopes_supported', ['read', 'write', 'admin']),

526

])

527

528

def handle_metadata(request):

529

"""Serve authorization server metadata."""

530

headers, body, status = metadata.create_metadata_response(

531

request.url, request.method

532

)

533

return Response(body, status=status, headers=headers)

534

```

535

536

## Server Configuration

537

538

### Token Expiration

539

540

```python

541

# Configure token expiration times

542

server = WebApplicationServer(

543

validator,

544

token_expires_in=3600, # Access tokens expire in 1 hour

545

)

546

547

# Or use a function for dynamic expiration

548

def token_expires_in(request):

549

if 'long_lived' in request.scopes:

550

return 86400 # 24 hours for long-lived tokens

551

return 3600 # 1 hour for regular tokens

552

553

server = WebApplicationServer(validator, token_expires_in=token_expires_in)

554

```

555

556

### Custom Token Generators

557

558

```python

559

from oauthlib.common import generate_token

560

import jwt

561

562

def custom_token_generator(request):

563

"""Generate JWT access tokens."""

564

payload = {

565

'user_id': request.user_id,

566

'client_id': request.client_id,

567

'scopes': request.scopes,

568

'exp': datetime.utcnow() + timedelta(hours=1)

569

}

570

return jwt.encode(payload, 'secret-key', algorithm='HS256')

571

572

server = WebApplicationServer(

573

validator,

574

token_generator=custom_token_generator,

575

refresh_token_generator=generate_token

576

)

577

```