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