or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

compliance-fixes.mdindex.mdoauth1.mdoauth2.md

oauth2.mddocs/

0

# OAuth 2.0 Implementation

1

2

Comprehensive OAuth 2.0 support with automatic token refresh, PKCE extension, compliance hooks, and support for all standard grant types. Provides both low-level token authentication and high-level workflow management.

3

4

## Capabilities

5

6

### OAuth2 Authentication Class

7

8

Low-level authentication handler that adds OAuth 2.0 tokens to requests. Implements the `requests.auth.AuthBase` interface for simple token-based authentication.

9

10

```python { .api }

11

class OAuth2(requests.auth.AuthBase):

12

def __init__(

13

self,

14

client_id: str = None,

15

client = None,

16

token: dict = None

17

):

18

"""

19

Create OAuth 2.0 authentication handler.

20

21

Args:

22

client_id (str, optional): Client ID from provider registration

23

client: oauthlib.oauth2.Client instance (default: WebApplicationClient)

24

token (dict, optional): Token dictionary with access_token and token_type

25

"""

26

```

27

28

**Usage Example:**

29

30

```python

31

import requests

32

from requests_oauthlib import OAuth2

33

34

# Create auth handler with token

35

auth = OAuth2(token={

36

'access_token': 'your_access_token',

37

'token_type': 'Bearer'

38

})

39

40

# Use with requests

41

response = requests.get('https://api.example.com/protected', auth=auth)

42

```

43

44

### OAuth2Session Workflow Class

45

46

High-level session class that extends `requests.Session` with comprehensive OAuth 2.0 workflow support, automatic token refresh, and provider compliance hooks.

47

48

```python { .api }

49

class OAuth2Session(requests.Session):

50

def __init__(

51

self,

52

client_id: str = None,

53

client = None,

54

auto_refresh_url: str = None,

55

auto_refresh_kwargs: dict = None,

56

scope: list = None,

57

redirect_uri: str = None,

58

token: dict = None,

59

state = None,

60

token_updater = None,

61

pkce: str = None,

62

**kwargs

63

):

64

"""

65

Create OAuth 2.0 session for workflow management.

66

67

Args:

68

client_id (str, optional): Client ID from provider

69

client: oauthlib.oauth2.Client instance (default: WebApplicationClient)

70

auto_refresh_url (str, optional): Token refresh endpoint URL

71

auto_refresh_kwargs (dict, optional): Extra arguments for token refresh

72

scope (list, optional): List of requested scopes

73

redirect_uri (str, optional): Registered callback URI

74

token (dict, optional): Initial token dictionary

75

state: CSRF protection string or callable

76

token_updater: Callback function for token updates

77

pkce (str, optional): PKCE method ("S256", "plain", or None)

78

"""

79

```

80

81

**Properties:**

82

83

```python { .api }

84

@property

85

def scope(self) -> list:

86

"""OAuth scopes for the session"""

87

88

@scope.setter

89

def scope(self, scope: list):

90

"""Set OAuth scopes"""

91

92

@property

93

def client_id(self) -> str:

94

"""Client identifier"""

95

96

@client_id.setter

97

def client_id(self, value: str):

98

"""Set client identifier"""

99

100

@client_id.deleter

101

def client_id(self):

102

"""Delete client identifier"""

103

104

@property

105

def token(self) -> dict:

106

"""Current token dictionary"""

107

108

@token.setter

109

def token(self, value: dict):

110

"""Set token and populate client attributes"""

111

112

@property

113

def access_token(self) -> str:

114

"""Current access token"""

115

116

@access_token.setter

117

def access_token(self, value: str):

118

"""Set access token"""

119

120

@access_token.deleter

121

def access_token(self):

122

"""Delete access token"""

123

124

@property

125

def authorized(self) -> bool:

126

"""True if session has valid access token"""

127

```

128

129

### Authorization Flow Methods

130

131

```python { .api }

132

def new_state(self) -> str:

133

"""

134

Generate new state string for CSRF protection.

135

136

Returns:

137

str: Generated state string

138

"""

139

140

def authorization_url(

141

self,

142

url: str,

143

state: str = None,

144

**kwargs

145

) -> tuple:

146

"""

147

Create authorization URL for user consent.

148

149

Args:

150

url (str): Authorization endpoint URL (must be HTTPS)

151

state (str, optional): CSRF protection state

152

**kwargs: Additional parameters for authorization URL

153

154

Returns:

155

tuple: (authorization_url, state)

156

"""

157

158

def fetch_token(

159

self,

160

token_url: str,

161

code: str = None,

162

authorization_response: str = None,

163

body: str = "",

164

auth = None,

165

username: str = None,

166

password: str = None,

167

method: str = "POST",

168

force_querystring: bool = False,

169

timeout = None,

170

headers: dict = None,

171

verify = None,

172

proxies = None,

173

include_client_id = None,

174

client_secret: str = None,

175

cert = None,

176

**kwargs

177

) -> dict:

178

"""

179

Fetch access token from token endpoint.

180

181

Args:

182

token_url (str): Token endpoint URL (must be HTTPS)

183

code (str, optional): Authorization code from callback

184

authorization_response (str, optional): Full callback URL

185

body (str): Additional request body content

186

auth: Authentication tuple or method

187

username (str, optional): Username for password grant

188

password (str, optional): Password for password grant

189

method (str): HTTP method (default: "POST")

190

force_querystring (bool): Force parameters in query string

191

timeout: Request timeout

192

headers (dict, optional): Additional request headers

193

verify: SSL certificate verification

194

proxies: Request proxies

195

include_client_id: Include client_id in request body

196

client_secret (str, optional): Client secret

197

cert: Client certificate for mTLS

198

**kwargs: Additional token request parameters

199

200

Returns:

201

dict: Token response from provider

202

203

Raises:

204

InsecureTransportError: If token_url is not HTTPS

205

ValueError: If required parameters are missing

206

"""

207

208

def token_from_fragment(self, authorization_response: str) -> dict:

209

"""

210

Parse token from URI fragment (for Implicit Grant).

211

212

Args:

213

authorization_response (str): Full callback URL with fragment

214

215

Returns:

216

dict: Parsed token data

217

"""

218

```

219

220

### Token Refresh Methods

221

222

```python { .api }

223

def refresh_token(

224

self,

225

token_url: str,

226

refresh_token: str = None,

227

body: str = "",

228

auth = None,

229

timeout = None,

230

headers: dict = None,

231

verify = None,

232

proxies = None,

233

**kwargs

234

) -> dict:

235

"""

236

Refresh access token using refresh token.

237

238

Args:

239

token_url (str): Refresh endpoint URL (must be HTTPS)

240

refresh_token (str, optional): Refresh token to use

241

body (str): Additional request body

242

auth: Authentication method

243

timeout: Request timeout

244

headers (dict, optional): Request headers

245

verify: SSL verification

246

proxies: Request proxies

247

**kwargs: Additional refresh parameters

248

249

Returns:

250

dict: New token response

251

252

Raises:

253

ValueError: If no token endpoint configured

254

InsecureTransportError: If token_url is not HTTPS

255

"""

256

```

257

258

### Request Methods

259

260

```python { .api }

261

def request(

262

self,

263

method: str,

264

url: str,

265

data = None,

266

headers: dict = None,

267

withhold_token: bool = False,

268

client_id: str = None,

269

client_secret: str = None,

270

files = None,

271

**kwargs

272

):

273

"""

274

Make authenticated HTTP request with automatic token handling.

275

276

Args:

277

method (str): HTTP method

278

url (str): Request URL (must be HTTPS)

279

data: Request body data

280

headers (dict, optional): Request headers

281

withhold_token (bool): Skip adding OAuth token

282

client_id (str, optional): Client ID for auto-refresh

283

client_secret (str, optional): Client secret for auto-refresh

284

files: File uploads

285

**kwargs: Additional request arguments

286

287

Returns:

288

Response: HTTP response object

289

290

Raises:

291

InsecureTransportError: If URL is not HTTPS

292

TokenExpiredError: If token expired and auto-refresh fails

293

TokenUpdated: If token was automatically refreshed (warning)

294

"""

295

```

296

297

### Compliance Hook System

298

299

```python { .api }

300

def register_compliance_hook(self, hook_type: str, hook):

301

"""

302

Register hook for request/response customization.

303

304

Args:

305

hook_type (str): Hook type identifier

306

hook: Callable to modify requests/responses

307

308

Hook Types:

309

- "access_token_response": Before token parsing

310

- "refresh_token_response": Before refresh token parsing

311

- "protected_request": Before making authenticated request

312

- "access_token_request": Before token fetch request

313

- "refresh_token_request": Before refresh request

314

315

Raises:

316

ValueError: If hook_type is not supported

317

"""

318

```

319

320

## Grant Type Examples

321

322

### Authorization Code Grant (Web Applications)

323

324

```python

325

from requests_oauthlib import OAuth2Session

326

327

# Step 1: Create session

328

oauth = OAuth2Session(

329

'client_id',

330

redirect_uri='https://example.com/callback',

331

scope=['read', 'write']

332

)

333

334

# Step 2: Get authorization URL

335

authorization_url = 'https://provider.com/oauth/authorize'

336

auth_url, state = oauth.authorization_url(authorization_url)

337

print(f"Go to: {auth_url}")

338

339

# Step 3: Exchange authorization code for token

340

token_url = 'https://provider.com/oauth/token'

341

token = oauth.fetch_token(

342

token_url,

343

authorization_response='https://example.com/callback?code=AUTH_CODE&state=STATE',

344

client_secret='client_secret'

345

)

346

347

# Step 4: Make authenticated requests

348

response = oauth.get('https://api.provider.com/user')

349

```

350

351

### Resource Owner Password Credentials Grant

352

353

```python

354

from requests_oauthlib import OAuth2Session

355

from oauthlib.oauth2 import LegacyApplicationClient

356

357

# Create session with password client

358

oauth = OAuth2Session(client=LegacyApplicationClient(client_id='client_id'))

359

360

# Fetch token using username/password

361

token = oauth.fetch_token(

362

token_url='https://provider.com/oauth/token',

363

username='user@example.com',

364

password='password',

365

client_id='client_id',

366

client_secret='client_secret'

367

)

368

369

# Make authenticated requests

370

response = oauth.get('https://api.provider.com/user')

371

```

372

373

### Client Credentials Grant

374

375

```python

376

from requests_oauthlib import OAuth2Session

377

from oauthlib.oauth2 import BackendApplicationClient

378

379

# Create session with backend client

380

oauth = OAuth2Session(client=BackendApplicationClient(client_id='client_id'))

381

382

# Fetch token using client credentials

383

token = oauth.fetch_token(

384

token_url='https://provider.com/oauth/token',

385

client_id='client_id',

386

client_secret='client_secret'

387

)

388

389

# Make authenticated requests

390

response = oauth.get('https://api.provider.com/data')

391

```

392

393

## PKCE Support

394

395

Proof Key for Code Exchange (PKCE) enhances security for public clients:

396

397

```python

398

from requests_oauthlib import OAuth2Session

399

400

# Enable PKCE with S256 method

401

oauth = OAuth2Session(

402

'client_id',

403

redirect_uri='https://example.com/callback',

404

scope=['read'],

405

pkce='S256' # or 'plain'

406

)

407

408

# Authorization URL automatically includes PKCE parameters

409

auth_url, state = oauth.authorization_url('https://provider.com/oauth/authorize')

410

411

# Token exchange automatically includes code_verifier

412

token = oauth.fetch_token(

413

'https://provider.com/oauth/token',

414

authorization_response=callback_url

415

)

416

```

417

418

## Automatic Token Refresh

419

420

Configure automatic token refresh for long-running applications:

421

422

```python

423

from requests_oauthlib import OAuth2Session

424

425

def save_token(token):

426

"""Save updated token to storage"""

427

print(f"Token updated: {token}")

428

429

oauth = OAuth2Session(

430

'client_id',

431

token=existing_token,

432

auto_refresh_url='https://provider.com/oauth/token',

433

auto_refresh_kwargs={'client_id': 'client_id', 'client_secret': 'client_secret'},

434

token_updater=save_token

435

)

436

437

# Automatically refreshes token if expired

438

response = oauth.get('https://api.provider.com/user')

439

```

440

441

## Exception Classes

442

443

```python { .api }

444

class TokenUpdated(Warning):

445

"""Warning raised when token is automatically refreshed"""

446

def __init__(self, token: dict):

447

"""

448

Args:

449

token (dict): New token dictionary

450

"""

451

```

452

453

## Security Considerations

454

455

- **HTTPS Enforcement**: All OAuth 2.0 endpoints must use HTTPS (enforced by library)

456

- **State Parameter**: Always use state parameter to prevent CSRF attacks

457

- **PKCE**: Use PKCE for public clients (mobile apps, SPAs)

458

- **Token Storage**: Store tokens securely and implement proper token lifecycle management

459

- **Scope Principle**: Request minimal necessary scopes

460

- **Token Expiration**: Implement proper token refresh workflows for long-running applications