OAuth2 Flow Examples
This page demonstrates various OAuth2 authentication flows using HTTP Client.
Client Credentials Flow
Basic Client Credentials
from requestforge import (
HttpClient,
HttpClientConfigBuilder,
TokenManager,
ClientCredentialsTokenProvider,
InMemoryTokenStorage
)
# Create OAuth2 provider
provider = ClientCredentialsTokenProvider(
token_url='https://auth.example.com/oauth/token',
client_id='your-client-id',
client_secret='your-client-secret',
service_name='example-api',
scope='read write'
)
# Create token manager
token_manager = TokenManager(
provider=provider,
storage=InMemoryTokenStorage()
)
# Configure HTTP client
config = (
HttpClientConfigBuilder()
.with_base_url('https://api.example.com')
.with_token_auth(token_manager=token_manager)
.build()
)
client = HttpClient(config)
# Token automatically fetched, cached, and injected
response = client.get('/users')
# Token automatically refreshed when expired
response = client.get('/posts')
With Django Cache
from requestforge import (
TokenManager,
ClientCredentialsTokenProvider,
DjangoCacheTokenStorage
)
# Provider
provider = ClientCredentialsTokenProvider(
token_url='https://auth.example.com/oauth/token',
client_id='your-client-id',
client_secret='your-client-secret',
service_name='example-api'
)
# Django cache storage (shared across processes)
storage = DjangoCacheTokenStorage(
cache_alias='default',
key_prefix='oauth_tokens'
)
# Token manager
token_manager = TokenManager(provider, storage)
# Django settings.py
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/1',
}
}
With Header Authentication
from requestforge import ClientCredentialsTokenProvider
# Send credentials in Authorization header (HTTP Basic)
provider = ClientCredentialsTokenProvider(
token_url='https://auth.example.com/oauth/token',
client_id='your-client-id',
client_secret='your-client-secret',
service_name='example-api',
auth_method='header' # Use HTTP Basic auth
)
With Extra Parameters
from requestforge import ClientCredentialsTokenProvider
provider = ClientCredentialsTokenProvider(
token_url='https://auth.example.com/oauth/token',
client_id='your-client-id',
client_secret='your-client-secret',
service_name='example-api',
scope='read write',
extra_params={
'audience': 'https://api.example.com',
'grant_type': 'client_credentials'
}
)
Password Grant Flow
User Authentication
from requestforge import (
TokenManager,
PasswordGrantTokenProvider,
InMemoryTokenStorage
)
# Password grant provider
provider = PasswordGrantTokenProvider(
token_url='https://auth.example.com/oauth/token',
client_id='your-client-id',
client_secret='your-client-secret',
username='user@example.com',
password='user-password',
service_name='example-api',
scope='profile email'
)
# Token manager
token_manager = TokenManager(
provider=provider,
storage=InMemoryTokenStorage()
)
# Configure client
config = (
HttpClientConfigBuilder()
.with_base_url('https://api.example.com')
.with_token_auth(token_manager=token_manager)
.build()
)
client = HttpClient(config)
response = client.get('/user/profile')
Token Refresh
Automatic Refresh
from requestforge import (
TokenManager,
ClientCredentialsTokenProvider
)
provider = ClientCredentialsTokenProvider(
token_url='https://auth.example.com/oauth/token',
client_id='your-client-id',
client_secret='your-client-secret',
service_name='example-api'
)
token_manager = TokenManager(provider)
# First call: fetches token
token = token_manager.get_token()
print(f"Token: {token.access_token}")
print(f"Expires: {token.expires_at}")
# Subsequent calls: returns cached token
token = token_manager.get_token()
# After expiration: automatically refreshes
token = token_manager.get_token()
Manual Refresh
from requestforge import TokenManager
token_manager = TokenManager(provider)
# Force refresh token
new_token = token_manager.force_refresh()
# Invalidate token
token_manager.invalidate_token()
# Next call will fetch new token
token = token_manager.get_token()
Auth Retry on 401
Automatic Token Refresh on 401
from requestforge import (
HttpClientConfigBuilder,
HttpClient,
TokenManager,
SimpleAuthRetryStrategy
)
# Token manager
token_manager = TokenManager(provider)
# Auth retry strategy
auth_retry = SimpleAuthRetryStrategy(
max_retries=1, # Retry once on 401
delay=0.5 # Wait 500ms before retry
)
# Configure client with auth retry
config = (
HttpClientConfigBuilder()
.with_base_url('https://api.example.com')
.with_token_auth(
token_manager=token_manager,
auth_retry_strategy=auth_retry
)
.build()
)
client = HttpClient(config)
# If 401 error:
# 1. Token manager refreshes token
# 2. Request retried with new token
response = client.get('/protected-resource')
Exponential Auth Retry
from requestforge import ExponentialAuthRetryStrategy
# Exponential backoff for auth retries
auth_retry = ExponentialAuthRetryStrategy(
max_retries=2,
base_delay=0.5,
max_delay=5.0,
multiplier=2.0,
jitter=True
)
config = (
HttpClientConfigBuilder()
.with_base_url('https://api.example.com')
.with_token_auth(
token_manager=token_manager,
auth_retry_strategy=auth_retry
)
.build()
)
Custom OAuth2 Provider
Custom Token Endpoint
from requestforge.interfaces import TokenProviderInterface
from requestforge.config import TokenData
from datetime import datetime, timedelta
import requests
class CustomOAuth2Provider(TokenProviderInterface):
"""Custom OAuth2 provider for proprietary API."""
def __init__(self, token_url, client_id, client_secret, service_name):
self._token_url = token_url
self._client_id = client_id
self._client_secret = client_secret
self._service_name = service_name
@property
def service_name(self):
return self._service_name
def fetch_token(self):
# Custom token fetch logic
response = requests.post(
self._token_url,
json={
'client_id': self._client_id,
'client_secret': self._client_secret,
'grant_type': 'custom_grant'
},
headers={'Content-Type': 'application/json'}
)
if response.status_code != 200:
raise AuthenticationException(
f'Token fetch failed: {response.status_code}',
service_name=self._service_name
)
data = response.json()
return TokenData(
access_token=data['access_token'],
token_type=data.get('token_type', 'Bearer'),
expires_at=datetime.now() + timedelta(seconds=data['expires_in']),
refresh_token=data.get('refresh_token'),
scope=data.get('scope')
)
def refresh_token(self, current_token):
if current_token.refresh_token:
# Use refresh token
response = requests.post(
self._token_url,
json={
'grant_type': 'refresh_token',
'refresh_token': current_token.refresh_token,
'client_id': self._client_id,
'client_secret': self._client_secret
}
)
if response.status_code == 200:
data = response.json()
return TokenData.from_response(data)
# Fallback to fetching new token
return self.fetch_token()
# Usage
provider = CustomOAuth2Provider(
token_url='https://auth.example.com/token',
client_id='client-id',
client_secret='client-secret',
service_name='custom-api'
)
token_manager = TokenManager(provider)
Multiple Services
Different Tokens for Different Services
from requestforge import (
HttpClient,
HttpClientConfigBuilder,
TokenManager,
ClientCredentialsTokenProvider
)
# Service A
service_a_provider = ClientCredentialsTokenProvider(
token_url='https://auth-a.example.com/token',
client_id='service-a-id',
client_secret='service-a-secret',
service_name='service-a'
)
service_a_manager = TokenManager(service_a_provider)
# Service B
service_b_provider = ClientCredentialsTokenProvider(
token_url='https://auth-b.example.com/token',
client_id='service-b-id',
client_secret='service-b-secret',
service_name='service-b'
)
service_b_manager = TokenManager(service_b_provider)
# Client for Service A
client_a = HttpClient(
HttpClientConfigBuilder()
.with_base_url('https://api-a.example.com')
.with_token_auth(token_manager=service_a_manager)
.build()
)
# Client for Service B
client_b = HttpClient(
HttpClientConfigBuilder()
.with_base_url('https://api-b.example.com')
.with_token_auth(token_manager=service_b_manager)
.build()
)
# Each client uses its own token
response_a = client_a.get('/data')
response_b = client_b.get('/data')
Path Exclusion
Exclude Paths from Authentication
from requestforge import HttpClientConfigBuilder
config = (
HttpClientConfigBuilder()
.with_base_url('https://api.example.com')
.with_token_auth(
token_manager=token_manager,
excluded_paths={
'/health', # Health check
'/status', # Status endpoint
'/public/docs' # Public documentation
}
)
.build()
)
client = HttpClient(config)
# No auth header
response = client.get('/health')
# With auth header
response = client.get('/protected')
Pattern-Based Exclusion
config = (
HttpClientConfigBuilder()
.with_base_url('https://api.example.com')
.with_token_auth(
token_manager=token_manager,
excluded_paths={'/health'},
excluded_path_patterns=[
'/public/*', # All public paths
'/docs/*', # All documentation
'*/health', # Health at any level
'/api/v*/status' # Status for any version
]
)
.build()
)
Token Inspection
Accessing Token Information
from requestforge import TokenManager
token_manager = TokenManager(provider)
# Get current token
token = token_manager.get_token()
print(f"Access Token: {token.access_token}")
print(f"Token Type: {token.token_type}")
print(f"Expires At: {token.expires_at}")
print(f"Refresh Token: {token.refresh_token}")
print(f"Scope: {token.scope}")
print(f"Is Expired: {token.is_expired}")
# Get authorization header value
auth_header = token.authorization_header
print(f"Authorization: {auth_header}")
Complete OAuth2 Example
Full OAuth2 Integration
from requestforge import (
HttpClient,
HttpClientConfigBuilder,
TokenManager,
ClientCredentialsTokenProvider,
DjangoCacheTokenStorage,
SimpleAuthRetryStrategy,
ExponentialBackoffRetryStrategy
)
import os
# OAuth2 provider
provider = ClientCredentialsTokenProvider(
token_url=os.getenv('OAUTH_TOKEN_URL'),
client_id=os.getenv('OAUTH_CLIENT_ID'),
client_secret=os.getenv('OAUTH_CLIENT_SECRET'),
service_name='my-api',
scope='read write delete'
)
# Token manager with Django cache
token_manager = TokenManager(
provider=provider,
storage=DjangoCacheTokenStorage(
cache_alias='default',
key_prefix='api_oauth_tokens'
)
)
# Auth retry strategy
auth_retry = SimpleAuthRetryStrategy(
max_retries=1,
delay=0.5
)
# Request retry strategy
request_retry = ExponentialBackoffRetryStrategy(
max_retries=3,
base_delay=1.0,
max_delay=60.0
)
# Configure client
config = (
HttpClientConfigBuilder()
.with_base_url(os.getenv('API_BASE_URL'))
.with_timeout(30.0)
.with_retry_strategy(request_retry)
.with_token_auth(
token_manager=token_manager,
auth_retry_strategy=auth_retry,
excluded_paths={'/health', '/metrics'},
excluded_path_patterns=['/public/*']
)
.with_logging(
log_headers=True,
log_body=False,
sensitive_keys={'authorization'}
)
.with_pool_connection(20)
.with_pool_maxsize(50)
.build()
)
client = HttpClient(config)
# Usage
try:
# Get users (token automatically managed)
response = client.get('/users')
users = response.json()
# Create user
response = client.post('/users', json_data={
'name': 'John Doe',
'email': 'john@example.com'
})
# Update user
response = client.put('/users/123', json_data={
'name': 'Jane Doe'
})
# Delete user
response = client.delete('/users/123')
except HttpClientException as e:
print(f"API error: {e}")
Error Handling
Handling OAuth2 Errors
from requestforge import (
AuthenticationException,
UnauthorizedException,
HttpClientException
)
try:
response = client.get('/protected-resource')
except AuthenticationException as e:
print(f"Authentication failed: {e.message}")
print(f"Service: {e.service_name}")
# Clear cached credentials or redirect to login
except UnauthorizedException:
print("Unauthorized - token may have been revoked")
except HttpClientException as e:
print(f"Request failed: {e}")
Token Revocation
from requestforge import TokenManager
token_manager = TokenManager(provider)
# Invalidate token (force refresh on next use)
token_manager.invalidate_token()
# Next call will fetch new token
token = token_manager.get_token()
Testing with OAuth2
Mocking OAuth2 Tokens
import pytest
from unittest.mock import Mock
from requestforge import TokenManager, TokenData
from datetime import datetime, timedelta
def test_oauth2_request():
# Mock provider
mock_provider = Mock()
mock_provider.fetch_token.return_value = TokenData(
access_token='test-token',
token_type='Bearer',
expires_at=datetime.now() + timedelta(hours=1)
)
mock_provider.service_name = 'test-service'
# Create token manager with mock
token_manager = TokenManager(mock_provider)
# Configure client
config = (
HttpClientConfigBuilder()
.with_base_url('https://api.example.com')
.with_token_auth(token_manager=token_manager)
.build()
)
client = HttpClient(config)
# Token provider called
token = token_manager.get_token()
assert token.access_token == 'test-token'
mock_provider.fetch_token.assert_called_once()
See Also
Multi-Step Authentication Examples - Multi-step authentication
Basic Requests Examples - Basic request examples
Authentication - Authentication guide
Token Manager API Reference - Token manager API