0
# Common Utilities
1
2
Shared utilities for encoding, security operations, URL handling, and error management used throughout the Authlib library. Provides consistent behavior, security best practices, and foundational functionality for all OAuth and JOSE operations.
3
4
## Capabilities
5
6
### Encoding and Serialization
7
8
Utilities for encoding, decoding, and serializing data in various formats used by OAuth and JOSE standards.
9
10
```python { .api }
11
def to_bytes(x: str, charset: str = "utf-8", errors: str = "strict") -> bytes:
12
"""
13
Convert string to bytes using specified encoding.
14
15
Args:
16
x: String to convert
17
charset: Character encoding to use
18
errors: Error handling strategy
19
20
Returns:
21
Bytes representation of the string
22
"""
23
24
def to_unicode(x, charset: str = "utf-8", errors: str = "strict") -> str:
25
"""
26
Convert bytes or string to unicode string.
27
28
Args:
29
x: Bytes or string to convert
30
charset: Character encoding to use
31
errors: Error handling strategy
32
33
Returns:
34
Unicode string representation
35
"""
36
37
def to_native(x, encoding: str = "ascii") -> str:
38
"""
39
Convert to native string type for the platform.
40
41
Args:
42
x: Input to convert
43
encoding: Character encoding to use
44
45
Returns:
46
Native string representation
47
"""
48
49
def json_loads(s: str) -> dict:
50
"""
51
Deserialize JSON string to Python object.
52
53
Args:
54
s: JSON string to deserialize
55
56
Returns:
57
Deserialized Python object
58
"""
59
60
def json_dumps(data: object, ensure_ascii: bool = False) -> str:
61
"""
62
Serialize Python object to JSON string.
63
64
Args:
65
data: Python object to serialize
66
ensure_ascii: Whether to escape non-ASCII characters
67
68
Returns:
69
JSON string representation
70
"""
71
72
def urlsafe_b64decode(s: str) -> bytes:
73
"""
74
Decode URL-safe base64 string.
75
76
Args:
77
s: URL-safe base64 string to decode
78
79
Returns:
80
Decoded bytes
81
"""
82
83
def urlsafe_b64encode(s: bytes) -> str:
84
"""
85
Encode bytes as URL-safe base64 string.
86
87
Args:
88
s: Bytes to encode
89
90
Returns:
91
URL-safe base64 string
92
"""
93
94
def base64_to_int(s: str) -> int:
95
"""
96
Convert base64 string to integer.
97
98
Args:
99
s: Base64 string representing an integer
100
101
Returns:
102
Integer value
103
"""
104
105
def int_to_base64(num: int) -> str:
106
"""
107
Convert integer to base64 string.
108
109
Args:
110
num: Integer to convert
111
112
Returns:
113
Base64 string representation
114
"""
115
116
def json_b64encode(text: str) -> str:
117
"""
118
JSON encode then base64 encode text.
119
120
Args:
121
text: Text to encode
122
123
Returns:
124
Base64-encoded JSON string
125
"""
126
```
127
128
### Security Utilities
129
130
Security functions for token generation, transport validation, and cryptographic operations.
131
132
```python { .api }
133
# Character set for token generation
134
UNICODE_ASCII_CHARACTER_SET: str
135
136
def generate_token(length: int = 30, chars: str = UNICODE_ASCII_CHARACTER_SET) -> str:
137
"""
138
Generate cryptographically secure random token.
139
140
Args:
141
length: Length of token to generate
142
chars: Character set to use for token generation
143
144
Returns:
145
Random token string
146
"""
147
148
def is_secure_transport(uri: str) -> bool:
149
"""
150
Check if URI uses secure transport (HTTPS).
151
152
Args:
153
uri: URI to check
154
155
Returns:
156
True if URI uses secure transport
157
"""
158
```
159
160
### URL Utilities
161
162
Comprehensive URL handling utilities for OAuth parameter encoding, decoding, and manipulation.
163
164
```python { .api }
165
def url_encode(params: dict) -> str:
166
"""
167
URL-encode parameters dictionary.
168
169
Args:
170
params: Dictionary of parameters to encode
171
172
Returns:
173
URL-encoded parameter string
174
"""
175
176
def url_decode(query: str) -> dict:
177
"""
178
URL-decode query string with validation.
179
180
Args:
181
query: Query string to decode
182
183
Returns:
184
Dictionary of decoded parameters
185
"""
186
187
def add_params_to_qs(query: str, params: dict) -> str:
188
"""
189
Add parameters to existing query string.
190
191
Args:
192
query: Existing query string
193
params: Parameters to add
194
195
Returns:
196
Updated query string
197
"""
198
199
def add_params_to_uri(uri: str, params: dict, fragment: bool = False) -> str:
200
"""
201
Add parameters to URI.
202
203
Args:
204
uri: Base URI
205
params: Parameters to add
206
fragment: Whether to add to fragment instead of query
207
208
Returns:
209
URI with added parameters
210
"""
211
212
def quote(s: str, safe: bytes = b"/") -> str:
213
"""
214
URL quote string with specified safe characters.
215
216
Args:
217
s: String to quote
218
safe: Characters to leave unquoted
219
220
Returns:
221
URL-quoted string
222
"""
223
224
def unquote(s: str) -> str:
225
"""
226
URL unquote string.
227
228
Args:
229
s: String to unquote
230
231
Returns:
232
Unquoted string
233
"""
234
235
def quote_url(s: str) -> str:
236
"""
237
Quote URL with extended safe characters for OAuth.
238
239
Args:
240
s: URL to quote
241
242
Returns:
243
Quoted URL string
244
"""
245
246
def extract_params(raw: object) -> dict:
247
"""
248
Extract parameters from various formats (dict, list, string).
249
250
Args:
251
raw: Raw parameter data
252
253
Returns:
254
Dictionary of extracted parameters
255
"""
256
257
def is_valid_url(url: str, fragments_allowed: bool = True) -> bool:
258
"""
259
Validate URL format.
260
261
Args:
262
url: URL to validate
263
fragments_allowed: Whether fragments (#) are allowed
264
265
Returns:
266
True if URL is valid
267
"""
268
```
269
270
### Error Handling
271
272
Comprehensive error handling classes for different OAuth and JOSE scenarios.
273
274
```python { .api }
275
class AuthlibBaseError(Exception):
276
"""
277
Base exception class for all Authlib errors.
278
279
Provides common error handling functionality for OAuth and JOSE operations.
280
"""
281
282
error: str = None
283
error_description: str = None
284
error_uri: str = None
285
286
def __init__(self, error: str = None, description: str = None, uri: str = None) -> None:
287
"""
288
Initialize base error.
289
290
Args:
291
error: Error code
292
description: Human-readable error description
293
uri: URI with error information
294
"""
295
296
class AuthlibHTTPError(AuthlibBaseError):
297
"""
298
HTTP-specific error class with status codes.
299
300
Used for OAuth and API errors that have HTTP status code semantics.
301
"""
302
303
status_code: int = 400
304
305
def __init__(self, error: str = None, description: str = None, uri: str = None, status_code: int = None) -> None:
306
"""
307
Initialize HTTP error.
308
309
Args:
310
error: Error code
311
description: Error description
312
uri: Error URI
313
status_code: HTTP status code
314
"""
315
316
class ContinueIteration(AuthlibBaseError):
317
"""
318
Special control flow exception for continuing iteration.
319
320
Used internally for flow control in OAuth grant processing.
321
"""
322
pass
323
```
324
325
### Request and Response Utilities
326
327
Utilities for handling HTTP requests and responses in OAuth flows.
328
329
```python { .api }
330
class OAuth2Request:
331
"""OAuth 2.0 request wrapper for framework-agnostic request handling."""
332
333
def __init__(self, method: str, uri: str, body: str = None, headers: dict = None) -> None:
334
"""
335
Initialize OAuth 2.0 request wrapper.
336
337
Args:
338
method: HTTP method
339
uri: Request URI
340
body: Request body
341
headers: HTTP headers dictionary
342
"""
343
344
class JsonRequest:
345
"""JSON request wrapper for API endpoints."""
346
347
def __init__(self, method: str, uri: str, body: str = None, headers: dict = None) -> None:
348
"""
349
Initialize JSON request wrapper.
350
351
Args:
352
method: HTTP method
353
uri: Request URI
354
body: JSON request body
355
headers: HTTP headers dictionary
356
"""
357
358
class OAuth2Payload:
359
"""OAuth 2.0 request payload wrapper."""
360
361
def __init__(self, params: dict = None) -> None:
362
"""
363
Initialize payload wrapper.
364
365
Args:
366
params: Payload parameters dictionary
367
"""
368
369
class JsonPayload:
370
"""JSON payload wrapper for structured data."""
371
372
def __init__(self, params: dict = None) -> None:
373
"""
374
Initialize JSON payload wrapper.
375
376
Args:
377
params: JSON payload parameters
378
"""
379
```
380
381
### Validation Utilities
382
383
Utilities for validating OAuth parameters, URIs, and other data.
384
385
```python { .api }
386
def validate_redirect_uri(redirect_uri: str, allowed_uris: list = None) -> bool:
387
"""
388
Validate OAuth redirect URI.
389
390
Args:
391
redirect_uri: Redirect URI to validate
392
allowed_uris: List of allowed redirect URIs
393
394
Returns:
395
True if redirect URI is valid
396
"""
397
398
def validate_scope(scope: str, allowed_scopes: list = None) -> bool:
399
"""
400
Validate OAuth scope parameter.
401
402
Args:
403
scope: Scope string to validate
404
allowed_scopes: List of allowed scopes
405
406
Returns:
407
True if scope is valid
408
"""
409
410
def validate_response_type(response_type: str, allowed_types: list = None) -> bool:
411
"""
412
Validate OAuth response type.
413
414
Args:
415
response_type: Response type to validate
416
allowed_types: List of allowed response types
417
418
Returns:
419
True if response type is valid
420
"""
421
422
def validate_grant_type(grant_type: str, allowed_grants: list = None) -> bool:
423
"""
424
Validate OAuth grant type.
425
426
Args:
427
grant_type: Grant type to validate
428
allowed_grants: List of allowed grant types
429
430
Returns:
431
True if grant type is valid
432
"""
433
```
434
435
## Usage Examples
436
437
### Token Generation
438
439
```python
440
from authlib.common.security import generate_token
441
442
# Generate random access token
443
access_token = generate_token(32)
444
print(f"Access token: {access_token}")
445
446
# Generate client secret
447
client_secret = generate_token(48)
448
print(f"Client secret: {client_secret}")
449
450
# Generate with custom character set
451
import string
452
alphanumeric = string.ascii_letters + string.digits
453
short_token = generate_token(16, alphanumeric)
454
print(f"Short token: {short_token}")
455
```
456
457
### URL Parameter Handling
458
459
```python
460
from authlib.common.urls import url_encode, url_decode, add_params_to_uri, is_valid_url
461
462
# Encode parameters for OAuth requests
463
params = {
464
'client_id': 'my-client-id',
465
'redirect_uri': 'https://example.com/callback',
466
'scope': 'read write',
467
'state': 'random-state-value'
468
}
469
query_string = url_encode(params)
470
print(f"Query string: {query_string}")
471
472
# Decode query string from callback
473
callback_query = "code=abc123&state=random-state-value"
474
decoded_params = url_decode(callback_query)
475
print(f"Decoded params: {decoded_params}")
476
477
# Add parameters to authorization URL
478
base_url = "https://provider.com/authorize"
479
auth_url = add_params_to_uri(base_url, params)
480
print(f"Authorization URL: {auth_url}")
481
482
# Validate redirect URIs
483
valid_uris = [
484
"https://example.com/callback",
485
"https://app.example.com/oauth/callback"
486
]
487
488
for uri in valid_uris:
489
if is_valid_url(uri):
490
print(f"Valid URI: {uri}")
491
```
492
493
### Encoding and Serialization
494
495
```python
496
from authlib.common.encoding import (
497
json_loads, json_dumps, urlsafe_b64encode, urlsafe_b64decode,
498
to_bytes, to_unicode
499
)
500
501
# JSON operations
502
data = {'user_id': 123, 'username': 'alice', 'scopes': ['read', 'write']}
503
json_string = json_dumps(data)
504
print(f"JSON: {json_string}")
505
506
parsed_data = json_loads(json_string)
507
print(f"Parsed: {parsed_data}")
508
509
# Base64 operations for JWT
510
payload = json_dumps(data)
511
payload_bytes = to_bytes(payload)
512
encoded_payload = urlsafe_b64encode(payload_bytes)
513
print(f"Base64 payload: {encoded_payload}")
514
515
# Decode
516
decoded_bytes = urlsafe_b64decode(encoded_payload)
517
decoded_string = to_unicode(decoded_bytes)
518
original_data = json_loads(decoded_string)
519
print(f"Original data: {original_data}")
520
```
521
522
### Error Handling
523
524
```python
525
from authlib.common.errors import AuthlibHTTPError, AuthlibBaseError
526
527
def validate_client_credentials(client_id, client_secret):
528
"""Example function that validates client credentials."""
529
530
if not client_id:
531
raise AuthlibHTTPError(
532
error='invalid_client',
533
description='Client ID is required',
534
status_code=400
535
)
536
537
if not client_secret:
538
raise AuthlibHTTPError(
539
error='invalid_client',
540
description='Client secret is required',
541
status_code=400
542
)
543
544
# Check credentials in database
545
client = get_client_by_id(client_id)
546
if not client or not client.check_secret(client_secret):
547
raise AuthlibHTTPError(
548
error='invalid_client',
549
description='Invalid client credentials',
550
status_code=401
551
)
552
553
return client
554
555
# Usage with error handling
556
try:
557
client = validate_client_credentials('client-id', 'wrong-secret')
558
except AuthlibHTTPError as error:
559
print(f"HTTP Error {error.status_code}: {error.error}")
560
print(f"Description: {error.error_description}")
561
except AuthlibBaseError as error:
562
print(f"Authlib Error: {error.error}")
563
```
564
565
### Security Validation
566
567
```python
568
from authlib.common.security import is_secure_transport
569
570
# Validate redirect URIs for security
571
redirect_uris = [
572
'https://example.com/callback', # Valid - HTTPS
573
'http://localhost:8080/callback', # Valid - localhost HTTP allowed
574
'http://example.com/callback', # Invalid - HTTP not allowed for production
575
'custom-app://oauth/callback' # Valid - custom scheme for mobile apps
576
]
577
578
for uri in redirect_uris:
579
if is_secure_transport(uri) or uri.startswith('http://localhost'):
580
print(f"✓ Secure: {uri}")
581
else:
582
print(f"✗ Insecure: {uri}")
583
```
584
585
### Request Wrapping
586
587
```python
588
from authlib.common.urls import extract_params
589
from authlib.oauth2 import OAuth2Request
590
591
# Create OAuth 2.0 request wrapper
592
def create_oauth_request(flask_request):
593
"""Convert Flask request to OAuth2Request."""
594
595
# Extract parameters from various sources
596
query_params = extract_params(flask_request.args)
597
form_params = extract_params(flask_request.form)
598
json_params = extract_params(flask_request.get_json() or {})
599
600
# Combine all parameters
601
all_params = {**query_params, **form_params, **json_params}
602
603
# Create OAuth request wrapper
604
oauth_request = OAuth2Request(
605
method=flask_request.method,
606
uri=flask_request.url,
607
body=flask_request.get_data(),
608
headers=dict(flask_request.headers)
609
)
610
611
# Add parsed parameters
612
oauth_request.data = all_params
613
614
return oauth_request
615
616
# Usage in Flask view
617
@app.route('/token', methods=['POST'])
618
def token_endpoint():
619
oauth_request = create_oauth_request(request)
620
621
# Process OAuth request
622
try:
623
response = authorization_server.create_token_response(oauth_request)
624
return response
625
except AuthlibHTTPError as error:
626
return {'error': error.error, 'error_description': error.error_description}, error.status_code
627
```
628
629
### Custom Validation
630
631
```python
632
def validate_oauth_parameters(request):
633
"""Validate common OAuth parameters."""
634
635
# Validate required parameters
636
required_params = ['client_id', 'response_type']
637
for param in required_params:
638
if not request.data.get(param):
639
raise AuthlibHTTPError(
640
error='invalid_request',
641
description=f'Missing required parameter: {param}'
642
)
643
644
# Validate redirect URI format
645
redirect_uri = request.data.get('redirect_uri')
646
if redirect_uri and not is_valid_url(redirect_uri):
647
raise AuthlibHTTPError(
648
error='invalid_request',
649
description='Invalid redirect URI format'
650
)
651
652
# Validate response type
653
response_type = request.data.get('response_type')
654
allowed_response_types = ['code', 'token', 'id_token']
655
if response_type not in allowed_response_types:
656
raise AuthlibHTTPError(
657
error='unsupported_response_type',
658
description=f'Response type "{response_type}" is not supported'
659
)
660
661
return True
662
663
# Usage in authorization endpoint
664
@app.route('/authorize')
665
def authorize():
666
oauth_request = create_oauth_request(request)
667
668
try:
669
validate_oauth_parameters(oauth_request)
670
# Continue with authorization flow
671
except AuthlibHTTPError as error:
672
return redirect(f"{oauth_request.data['redirect_uri']}?error={error.error}&error_description={error.error_description}")
673
```