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-clients.mddocs/

0

# OAuth 2.0 Clients

1

2

OAuth 2.0 client implementations supporting all standard grant types including authorization code, implicit, client credentials, and password flows. Includes PKCE support for enhanced security and specialized clients for different application architectures.

3

4

## Capabilities

5

6

### Base Client

7

8

Base OAuth 2.0 client providing core functionality for all grant types. Handles token management, request preparation, and response parsing with support for various token placement strategies.

9

10

```python { .api }

11

class Client:

12

def __init__(

13

self,

14

client_id: str,

15

default_token_placement: str = "auth_header",

16

token_type: str = "Bearer",

17

access_token: str | None = None,

18

refresh_token: str | None = None,

19

mac_key: str | bytes | bytearray | None = None,

20

mac_algorithm: str | None = None,

21

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

22

scope: str | list[str] | None = None,

23

state: str | None = None,

24

redirect_url: str | None = None,

25

state_generator: callable = ...,

26

code_verifier: str | None = None,

27

code_challenge: str | None = None,

28

code_challenge_method: str | None = None,

29

**kwargs,

30

):

31

"""

32

Base OAuth 2.0 client.

33

34

Parameters:

35

- client_id: Client identifier

36

- default_token_placement: Where to place access token (auth_header, query, body)

37

- token_type: Token type (Bearer, MAC)

38

- access_token: Current access token

39

- refresh_token: Current refresh token

40

- mac_key: MAC authentication key

41

- mac_algorithm: MAC algorithm

42

- token: Token dictionary

43

- scope: Access scope

44

- state: State parameter for CSRF protection

45

- redirect_url: Redirect URL

46

- state_generator: Function to generate state values

47

- code_verifier: PKCE code verifier

48

- code_challenge: PKCE code challenge

49

- code_challenge_method: PKCE challenge method (plain, S256)

50

"""

51

52

def add_token(

53

self,

54

uri: str,

55

http_method: str = "GET",

56

body: str | None = None,

57

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

58

token_placement: str | None = None,

59

**kwargs,

60

) -> tuple[str, dict[str, str] | None, str | None]:

61

"""

62

Add access token to request.

63

64

Returns:

65

Tuple of (uri, headers, body) with token applied

66

"""

67

68

def prepare_authorization_request(

69

self,

70

authorization_url: str,

71

state: str | None = None,

72

redirect_url: str | None = None,

73

scope: str | list[str] | None = None,

74

**kwargs,

75

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

76

"""

77

Prepare authorization request URL.

78

79

Returns:

80

Tuple of (url, headers, body) for authorization request

81

"""

82

83

def prepare_token_request(

84

self,

85

token_url: str,

86

authorization_response: str | None = None,

87

redirect_url: str | None = None,

88

state: str | None = None,

89

body: str = "",

90

**kwargs,

91

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

92

"""

93

Prepare token request.

94

95

Returns:

96

Tuple of (url, headers, body) for token request

97

"""

98

99

def prepare_refresh_token_request(

100

self,

101

token_url: str,

102

refresh_token: str | None = None,

103

body: str = "",

104

scope: str | list[str] | None = None,

105

**kwargs,

106

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

107

"""

108

Prepare refresh token request.

109

110

Returns:

111

Tuple of (url, headers, body) for refresh request

112

"""

113

114

def prepare_token_revocation_request(

115

self,

116

revocation_url: str,

117

token: str,

118

token_type_hint: str | None = "access_token",

119

body: str = "",

120

callback: callable | None = None,

121

**kwargs,

122

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

123

"""

124

Prepare token revocation request.

125

126

Parameters:

127

- revocation_url: Token revocation endpoint URL

128

- token: Token to revoke

129

- token_type_hint: Type of token (access_token, refresh_token)

130

- body: Request body

131

- callback: Callback function

132

133

Returns:

134

Tuple of (url, headers, body) for revocation request

135

"""

136

137

def parse_request_body_response(

138

self,

139

body: str,

140

scope: str | list[str] | None = None,

141

**kwargs,

142

) -> dict[str, str]:

143

"""Parse token response body."""

144

145

def parse_request_uri_response(self, *args, **kwargs) -> dict[str, str]:

146

"""Parse response from URI."""

147

148

def prepare_refresh_body(

149

self,

150

body: str = "",

151

refresh_token: str | None = None,

152

scope: str | list[str] | None = None,

153

**kwargs,

154

) -> str:

155

"""Prepare refresh token request body."""

156

157

# PKCE methods

158

def create_code_verifier(self, length: int) -> str:

159

"""Create PKCE code verifier."""

160

161

def create_code_challenge(

162

self,

163

code_verifier: str,

164

code_challenge_method: str | None = None,

165

) -> str:

166

"""Create PKCE code challenge."""

167

168

# Token management

169

def populate_code_attributes(self, response: dict[str, str]) -> None:

170

"""Populate authorization code attributes from response."""

171

172

def populate_token_attributes(self, response: dict[str, str]) -> None:

173

"""Populate token attributes from response."""

174

```

175

176

### Web Application Client

177

178

OAuth 2.0 client for web applications using the authorization code grant. Provides the most secure flow suitable for server-side applications that can securely store client credentials.

179

180

```python { .api }

181

class WebApplicationClient(Client):

182

def __init__(

183

self,

184

client_id: str,

185

code: str | None = None,

186

*,

187

default_token_placement: str = "auth_header",

188

token_type: str = "Bearer",

189

access_token: str | None = None,

190

refresh_token: str | None = None,

191

mac_key: str | bytes | bytearray | None = None,

192

mac_algorithm: str | None = None,

193

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

194

scope: str | list[str] | None = None,

195

state: str | None = None,

196

redirect_url: str | None = None,

197

state_generator: callable = ...,

198

code_verifier: str | None = None,

199

code_challenge: str | None = None,

200

code_challenge_method: str | None = None,

201

**kwargs,

202

):

203

"""

204

Web application client for authorization code grant.

205

206

Suitable for server-side applications that can securely store

207

client credentials and handle redirect URIs.

208

"""

209

210

def prepare_request_uri(

211

self,

212

uri: str,

213

redirect_uri: str | None = None,

214

scope: str | list[str] | None = None,

215

state: str | None = None,

216

code_challenge: str | None = None,

217

code_challenge_method: str = "plain",

218

**kwargs,

219

) -> str:

220

"""Prepare authorization request URI."""

221

222

def prepare_request_body(

223

self,

224

code: str | None = None,

225

redirect_uri: str | None = None,

226

body: str = "",

227

include_client_id: bool = True,

228

code_verifier: str | None = None,

229

*,

230

scope: str | list[str] | None = None,

231

client_id: str | None = None,

232

client_secret: str | None = None,

233

**kwargs,

234

) -> str:

235

"""Prepare token request body with authorization code."""

236

237

def parse_request_uri_response(self, uri: str, state: str | None = None) -> dict[str, str]:

238

"""Parse authorization response from redirect URI."""

239

```

240

241

Usage example:

242

243

```python

244

from oauthlib.oauth2 import WebApplicationClient

245

import requests

246

247

# Create client

248

client = WebApplicationClient('your-client-id')

249

250

# Step 1: Get authorization URL

251

auth_url = 'https://auth.example.com/authorize'

252

authorization_url, headers, body = client.prepare_authorization_request(

253

auth_url,

254

redirect_url='https://your-app.com/callback',

255

scope=['read', 'write'],

256

state='random-state-value'

257

)

258

259

# User visits authorization_url and gets redirected back with code

260

261

# Step 2: Exchange code for token

262

token_url = 'https://auth.example.com/token'

263

token_request_url, headers, body = client.prepare_token_request(

264

token_url,

265

authorization_response='https://your-app.com/callback?code=ABC123&state=random-state-value',

266

redirect_url='https://your-app.com/callback'

267

)

268

269

# Make token request

270

response = requests.post(token_request_url, headers=headers, data=body)

271

token = client.parse_request_body_response(response.text)

272

273

# Step 3: Use access token

274

api_url, headers, body = client.add_token('https://api.example.com/data')

275

api_response = requests.get(api_url, headers=headers)

276

```

277

278

### Mobile Application Client

279

280

OAuth 2.0 client for mobile and single-page applications using the implicit grant. Designed for public clients that cannot securely store credentials.

281

282

```python { .api }

283

class MobileApplicationClient(Client):

284

def __init__(self, client_id: str, **kwargs):

285

"""

286

Mobile application client for implicit grant.

287

288

Suitable for public clients like mobile apps and single-page

289

applications that cannot securely store client credentials.

290

"""

291

292

def prepare_request_uri(

293

self,

294

uri: str,

295

redirect_uri: str | None = None,

296

scope: str | list[str] | None = None,

297

state: str | None = None,

298

**kwargs,

299

) -> str:

300

"""Prepare implicit grant authorization request URI."""

301

302

def parse_request_uri_response(

303

self,

304

uri: str,

305

state: str | None = None,

306

scope: str | list[str] | None = None,

307

) -> dict[str, str]:

308

"""Parse implicit grant response from redirect URI fragment."""

309

```

310

311

### Legacy Application Client

312

313

OAuth 2.0 client for legacy applications using the resource owner password credentials grant. Should only be used when other flows are not feasible.

314

315

```python { .api }

316

class LegacyApplicationClient(Client):

317

def __init__(self, client_id: str, **kwargs):

318

"""

319

Legacy application client for password credentials grant.

320

321

Only use when authorization code or implicit grants are not feasible.

322

Requires high trust between client and authorization server.

323

"""

324

325

def prepare_request_body(

326

self,

327

username: str,

328

password: str,

329

scope: str | list[str] | None = None,

330

**kwargs,

331

) -> str:

332

"""

333

Prepare password credentials token request body.

334

335

Parameters:

336

- username: Resource owner username

337

- password: Resource owner password

338

- scope: Requested scope

339

"""

340

```

341

342

### Backend Application Client

343

344

OAuth 2.0 client for backend applications using the client credentials grant. Used for machine-to-machine authentication without user involvement.

345

346

```python { .api }

347

class BackendApplicationClient(Client):

348

def __init__(self, client_id: str, **kwargs):

349

"""

350

Backend application client for client credentials grant.

351

352

Used for machine-to-machine authentication where the client

353

is acting on its own behalf rather than on behalf of a user.

354

"""

355

356

def prepare_request_body(

357

self,

358

scope: str | list[str] | None = None,

359

**kwargs,

360

) -> str:

361

"""

362

Prepare client credentials token request body.

363

364

Parameters:

365

- scope: Requested scope

366

"""

367

```

368

369

### Service Application Client

370

371

OAuth 2.0 client for service applications using JWT bearer assertion grant. Used for secure server-to-server communication with JWT authentication.

372

373

```python { .api }

374

class ServiceApplicationClient(Client):

375

def __init__(self, client_id: str, **kwargs):

376

"""

377

Service application client for JWT bearer assertion grant.

378

379

Used for secure server-to-server communication using

380

JSON Web Tokens for client authentication.

381

"""

382

383

def prepare_request_body(

384

self,

385

private_key: str,

386

subject: str,

387

issuer: str,

388

audience: str,

389

expires_at: int | None = None,

390

issued_at: int | None = None,

391

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

392

scope: str | list[str] | None = None,

393

**kwargs,

394

) -> str:

395

"""

396

Prepare JWT bearer assertion token request body.

397

398

Parameters:

399

- private_key: Private key for JWT signing

400

- subject: Subject of the JWT

401

- issuer: Issuer of the JWT

402

- audience: Audience of the JWT

403

- expires_at: Expiration time

404

- issued_at: Issued at time

405

- extra_claims: Additional JWT claims

406

- scope: Requested scope

407

"""

408

```

409

410

### Device Client

411

412

OAuth 2.0 client for device flow (RFC 8628) used by input-constrained devices. Enables authentication on devices without web browsers or with limited input capabilities.

413

414

```python { .api }

415

class DeviceClient(Client):

416

def __init__(self, client_id: str, **kwargs):

417

"""

418

Device client for device authorization grant (RFC 8628).

419

420

Used by input-constrained devices like smart TVs, IoT devices,

421

and command-line applications.

422

"""

423

424

def prepare_request_uri(

425

self,

426

uri: str,

427

scope: str | list[str] | None = None,

428

**kwargs,

429

) -> str:

430

"""Prepare device authorization request URI."""

431

432

def prepare_request_body(

433

self,

434

device_code: str,

435

**kwargs,

436

) -> str:

437

"""

438

Prepare device token request body.

439

440

Parameters:

441

- device_code: Device code from device authorization response

442

"""

443

444

def parse_request_uri_response(self, uri: str, state: str | None = None) -> dict[str, str]:

445

"""Parse device authorization response."""

446

```

447

448

## Token Placement Constants

449

450

```python { .api }

451

# Token placement options

452

AUTH_HEADER: str # Place token in Authorization header

453

URI_QUERY: str # Place token in URI query parameters

454

BODY: str # Place token in request body

455

456

# Form encoding headers

457

FORM_ENC_HEADERS: dict[str, str]

458

```

459

460

## Usage Patterns

461

462

### Authorization Code Flow with PKCE

463

464

```python

465

from oauthlib.oauth2 import WebApplicationClient

466

from oauthlib.common import generate_token

467

import requests

468

import secrets

469

import hashlib

470

import base64

471

472

# Create client with PKCE

473

client = WebApplicationClient('your-client-id')

474

475

# Generate PKCE parameters

476

code_verifier = client.create_code_verifier(128)

477

code_challenge = client.create_code_challenge(code_verifier, 'S256')

478

479

# Step 1: Authorization request

480

auth_url, headers, body = client.prepare_authorization_request(

481

'https://auth.example.com/authorize',

482

redirect_url='https://your-app.com/callback',

483

scope=['read', 'write'],

484

state=generate_token(),

485

code_challenge=code_challenge,

486

code_challenge_method='S256'

487

)

488

489

# Step 2: Token exchange with PKCE

490

token_url, headers, body = client.prepare_token_request(

491

'https://auth.example.com/token',

492

authorization_response='callback_url_with_code',

493

redirect_url='https://your-app.com/callback',

494

code_verifier=code_verifier

495

)

496

```

497

498

### Client Credentials Flow

499

500

```python

501

from oauthlib.oauth2 import BackendApplicationClient

502

import requests

503

504

# Create client

505

client = BackendApplicationClient('your-client-id')

506

507

# Prepare token request

508

token_url, headers, body = client.prepare_token_request(

509

'https://auth.example.com/token',

510

scope=['api:read', 'api:write']

511

)

512

513

# Add client authentication to headers

514

headers['Authorization'] = 'Basic ' + base64.b64encode(

515

f'{client_id}:{client_secret}'.encode()

516

).decode()

517

518

# Get token

519

response = requests.post(token_url, headers=headers, data=body)

520

token = client.parse_request_body_response(response.text)

521

522

# Use token

523

api_url, headers, body = client.add_token('https://api.example.com/data')

524

api_response = requests.get(api_url, headers=headers)

525

```

526

527

### Token Refresh

528

529

```python

530

# Refresh expired token

531

if token.get('refresh_token'):

532

refresh_url, headers, body = client.prepare_refresh_token_request(

533

'https://auth.example.com/token',

534

refresh_token=token['refresh_token'],

535

scope=['read'] # Optional: reduced scope

536

)

537

538

response = requests.post(refresh_url, headers=headers, data=body)

539

new_token = client.parse_request_body_response(response.text)

540

```