The ultimate Python library in building OAuth and OpenID Connect servers and clients.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
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.
Utilities for encoding, decoding, and serializing data in various formats used by OAuth and JOSE standards.
def to_bytes(x: str, charset: str = "utf-8", errors: str = "strict") -> bytes:
"""
Convert string to bytes using specified encoding.
Args:
x: String to convert
charset: Character encoding to use
errors: Error handling strategy
Returns:
Bytes representation of the string
"""
def to_unicode(x, charset: str = "utf-8", errors: str = "strict") -> str:
"""
Convert bytes or string to unicode string.
Args:
x: Bytes or string to convert
charset: Character encoding to use
errors: Error handling strategy
Returns:
Unicode string representation
"""
def to_native(x, encoding: str = "ascii") -> str:
"""
Convert to native string type for the platform.
Args:
x: Input to convert
encoding: Character encoding to use
Returns:
Native string representation
"""
def json_loads(s: str) -> dict:
"""
Deserialize JSON string to Python object.
Args:
s: JSON string to deserialize
Returns:
Deserialized Python object
"""
def json_dumps(data: object, ensure_ascii: bool = False) -> str:
"""
Serialize Python object to JSON string.
Args:
data: Python object to serialize
ensure_ascii: Whether to escape non-ASCII characters
Returns:
JSON string representation
"""
def urlsafe_b64decode(s: str) -> bytes:
"""
Decode URL-safe base64 string.
Args:
s: URL-safe base64 string to decode
Returns:
Decoded bytes
"""
def urlsafe_b64encode(s: bytes) -> str:
"""
Encode bytes as URL-safe base64 string.
Args:
s: Bytes to encode
Returns:
URL-safe base64 string
"""
def base64_to_int(s: str) -> int:
"""
Convert base64 string to integer.
Args:
s: Base64 string representing an integer
Returns:
Integer value
"""
def int_to_base64(num: int) -> str:
"""
Convert integer to base64 string.
Args:
num: Integer to convert
Returns:
Base64 string representation
"""
def json_b64encode(text: str) -> str:
"""
JSON encode then base64 encode text.
Args:
text: Text to encode
Returns:
Base64-encoded JSON string
"""Security functions for token generation, transport validation, and cryptographic operations.
# Character set for token generation
UNICODE_ASCII_CHARACTER_SET: str
def generate_token(length: int = 30, chars: str = UNICODE_ASCII_CHARACTER_SET) -> str:
"""
Generate cryptographically secure random token.
Args:
length: Length of token to generate
chars: Character set to use for token generation
Returns:
Random token string
"""
def is_secure_transport(uri: str) -> bool:
"""
Check if URI uses secure transport (HTTPS).
Args:
uri: URI to check
Returns:
True if URI uses secure transport
"""Comprehensive URL handling utilities for OAuth parameter encoding, decoding, and manipulation.
def url_encode(params: dict) -> str:
"""
URL-encode parameters dictionary.
Args:
params: Dictionary of parameters to encode
Returns:
URL-encoded parameter string
"""
def url_decode(query: str) -> dict:
"""
URL-decode query string with validation.
Args:
query: Query string to decode
Returns:
Dictionary of decoded parameters
"""
def add_params_to_qs(query: str, params: dict) -> str:
"""
Add parameters to existing query string.
Args:
query: Existing query string
params: Parameters to add
Returns:
Updated query string
"""
def add_params_to_uri(uri: str, params: dict, fragment: bool = False) -> str:
"""
Add parameters to URI.
Args:
uri: Base URI
params: Parameters to add
fragment: Whether to add to fragment instead of query
Returns:
URI with added parameters
"""
def quote(s: str, safe: bytes = b"/") -> str:
"""
URL quote string with specified safe characters.
Args:
s: String to quote
safe: Characters to leave unquoted
Returns:
URL-quoted string
"""
def unquote(s: str) -> str:
"""
URL unquote string.
Args:
s: String to unquote
Returns:
Unquoted string
"""
def quote_url(s: str) -> str:
"""
Quote URL with extended safe characters for OAuth.
Args:
s: URL to quote
Returns:
Quoted URL string
"""
def extract_params(raw: object) -> dict:
"""
Extract parameters from various formats (dict, list, string).
Args:
raw: Raw parameter data
Returns:
Dictionary of extracted parameters
"""
def is_valid_url(url: str, fragments_allowed: bool = True) -> bool:
"""
Validate URL format.
Args:
url: URL to validate
fragments_allowed: Whether fragments (#) are allowed
Returns:
True if URL is valid
"""Comprehensive error handling classes for different OAuth and JOSE scenarios.
class AuthlibBaseError(Exception):
"""
Base exception class for all Authlib errors.
Provides common error handling functionality for OAuth and JOSE operations.
"""
error: str = None
error_description: str = None
error_uri: str = None
def __init__(self, error: str = None, description: str = None, uri: str = None) -> None:
"""
Initialize base error.
Args:
error: Error code
description: Human-readable error description
uri: URI with error information
"""
class AuthlibHTTPError(AuthlibBaseError):
"""
HTTP-specific error class with status codes.
Used for OAuth and API errors that have HTTP status code semantics.
"""
status_code: int = 400
def __init__(self, error: str = None, description: str = None, uri: str = None, status_code: int = None) -> None:
"""
Initialize HTTP error.
Args:
error: Error code
description: Error description
uri: Error URI
status_code: HTTP status code
"""
class ContinueIteration(AuthlibBaseError):
"""
Special control flow exception for continuing iteration.
Used internally for flow control in OAuth grant processing.
"""
passUtilities for handling HTTP requests and responses in OAuth flows.
class OAuth2Request:
"""OAuth 2.0 request wrapper for framework-agnostic request handling."""
def __init__(self, method: str, uri: str, body: str = None, headers: dict = None) -> None:
"""
Initialize OAuth 2.0 request wrapper.
Args:
method: HTTP method
uri: Request URI
body: Request body
headers: HTTP headers dictionary
"""
class JsonRequest:
"""JSON request wrapper for API endpoints."""
def __init__(self, method: str, uri: str, body: str = None, headers: dict = None) -> None:
"""
Initialize JSON request wrapper.
Args:
method: HTTP method
uri: Request URI
body: JSON request body
headers: HTTP headers dictionary
"""
class OAuth2Payload:
"""OAuth 2.0 request payload wrapper."""
def __init__(self, params: dict = None) -> None:
"""
Initialize payload wrapper.
Args:
params: Payload parameters dictionary
"""
class JsonPayload:
"""JSON payload wrapper for structured data."""
def __init__(self, params: dict = None) -> None:
"""
Initialize JSON payload wrapper.
Args:
params: JSON payload parameters
"""Utilities for validating OAuth parameters, URIs, and other data.
def validate_redirect_uri(redirect_uri: str, allowed_uris: list = None) -> bool:
"""
Validate OAuth redirect URI.
Args:
redirect_uri: Redirect URI to validate
allowed_uris: List of allowed redirect URIs
Returns:
True if redirect URI is valid
"""
def validate_scope(scope: str, allowed_scopes: list = None) -> bool:
"""
Validate OAuth scope parameter.
Args:
scope: Scope string to validate
allowed_scopes: List of allowed scopes
Returns:
True if scope is valid
"""
def validate_response_type(response_type: str, allowed_types: list = None) -> bool:
"""
Validate OAuth response type.
Args:
response_type: Response type to validate
allowed_types: List of allowed response types
Returns:
True if response type is valid
"""
def validate_grant_type(grant_type: str, allowed_grants: list = None) -> bool:
"""
Validate OAuth grant type.
Args:
grant_type: Grant type to validate
allowed_grants: List of allowed grant types
Returns:
True if grant type is valid
"""from authlib.common.security import generate_token
# Generate random access token
access_token = generate_token(32)
print(f"Access token: {access_token}")
# Generate client secret
client_secret = generate_token(48)
print(f"Client secret: {client_secret}")
# Generate with custom character set
import string
alphanumeric = string.ascii_letters + string.digits
short_token = generate_token(16, alphanumeric)
print(f"Short token: {short_token}")from authlib.common.urls import url_encode, url_decode, add_params_to_uri, is_valid_url
# Encode parameters for OAuth requests
params = {
'client_id': 'my-client-id',
'redirect_uri': 'https://example.com/callback',
'scope': 'read write',
'state': 'random-state-value'
}
query_string = url_encode(params)
print(f"Query string: {query_string}")
# Decode query string from callback
callback_query = "code=abc123&state=random-state-value"
decoded_params = url_decode(callback_query)
print(f"Decoded params: {decoded_params}")
# Add parameters to authorization URL
base_url = "https://provider.com/authorize"
auth_url = add_params_to_uri(base_url, params)
print(f"Authorization URL: {auth_url}")
# Validate redirect URIs
valid_uris = [
"https://example.com/callback",
"https://app.example.com/oauth/callback"
]
for uri in valid_uris:
if is_valid_url(uri):
print(f"Valid URI: {uri}")from authlib.common.encoding import (
json_loads, json_dumps, urlsafe_b64encode, urlsafe_b64decode,
to_bytes, to_unicode
)
# JSON operations
data = {'user_id': 123, 'username': 'alice', 'scopes': ['read', 'write']}
json_string = json_dumps(data)
print(f"JSON: {json_string}")
parsed_data = json_loads(json_string)
print(f"Parsed: {parsed_data}")
# Base64 operations for JWT
payload = json_dumps(data)
payload_bytes = to_bytes(payload)
encoded_payload = urlsafe_b64encode(payload_bytes)
print(f"Base64 payload: {encoded_payload}")
# Decode
decoded_bytes = urlsafe_b64decode(encoded_payload)
decoded_string = to_unicode(decoded_bytes)
original_data = json_loads(decoded_string)
print(f"Original data: {original_data}")from authlib.common.errors import AuthlibHTTPError, AuthlibBaseError
def validate_client_credentials(client_id, client_secret):
"""Example function that validates client credentials."""
if not client_id:
raise AuthlibHTTPError(
error='invalid_client',
description='Client ID is required',
status_code=400
)
if not client_secret:
raise AuthlibHTTPError(
error='invalid_client',
description='Client secret is required',
status_code=400
)
# Check credentials in database
client = get_client_by_id(client_id)
if not client or not client.check_secret(client_secret):
raise AuthlibHTTPError(
error='invalid_client',
description='Invalid client credentials',
status_code=401
)
return client
# Usage with error handling
try:
client = validate_client_credentials('client-id', 'wrong-secret')
except AuthlibHTTPError as error:
print(f"HTTP Error {error.status_code}: {error.error}")
print(f"Description: {error.error_description}")
except AuthlibBaseError as error:
print(f"Authlib Error: {error.error}")from authlib.common.security import is_secure_transport
# Validate redirect URIs for security
redirect_uris = [
'https://example.com/callback', # Valid - HTTPS
'http://localhost:8080/callback', # Valid - localhost HTTP allowed
'http://example.com/callback', # Invalid - HTTP not allowed for production
'custom-app://oauth/callback' # Valid - custom scheme for mobile apps
]
for uri in redirect_uris:
if is_secure_transport(uri) or uri.startswith('http://localhost'):
print(f"✓ Secure: {uri}")
else:
print(f"✗ Insecure: {uri}")from authlib.common.urls import extract_params
from authlib.oauth2 import OAuth2Request
# Create OAuth 2.0 request wrapper
def create_oauth_request(flask_request):
"""Convert Flask request to OAuth2Request."""
# Extract parameters from various sources
query_params = extract_params(flask_request.args)
form_params = extract_params(flask_request.form)
json_params = extract_params(flask_request.get_json() or {})
# Combine all parameters
all_params = {**query_params, **form_params, **json_params}
# Create OAuth request wrapper
oauth_request = OAuth2Request(
method=flask_request.method,
uri=flask_request.url,
body=flask_request.get_data(),
headers=dict(flask_request.headers)
)
# Add parsed parameters
oauth_request.data = all_params
return oauth_request
# Usage in Flask view
@app.route('/token', methods=['POST'])
def token_endpoint():
oauth_request = create_oauth_request(request)
# Process OAuth request
try:
response = authorization_server.create_token_response(oauth_request)
return response
except AuthlibHTTPError as error:
return {'error': error.error, 'error_description': error.error_description}, error.status_codedef validate_oauth_parameters(request):
"""Validate common OAuth parameters."""
# Validate required parameters
required_params = ['client_id', 'response_type']
for param in required_params:
if not request.data.get(param):
raise AuthlibHTTPError(
error='invalid_request',
description=f'Missing required parameter: {param}'
)
# Validate redirect URI format
redirect_uri = request.data.get('redirect_uri')
if redirect_uri and not is_valid_url(redirect_uri):
raise AuthlibHTTPError(
error='invalid_request',
description='Invalid redirect URI format'
)
# Validate response type
response_type = request.data.get('response_type')
allowed_response_types = ['code', 'token', 'id_token']
if response_type not in allowed_response_types:
raise AuthlibHTTPError(
error='unsupported_response_type',
description=f'Response type "{response_type}" is not supported'
)
return True
# Usage in authorization endpoint
@app.route('/authorize')
def authorize():
oauth_request = create_oauth_request(request)
try:
validate_oauth_parameters(oauth_request)
# Continue with authorization flow
except AuthlibHTTPError as error:
return redirect(f"{oauth_request.data['redirect_uri']}?error={error.error}&error_description={error.error_description}")Install with Tessl CLI
npx tessl i tessl/pypi-authlib