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
```