Backend Integration
Learn how to integrate Stack Auth's backend into your Python application
To authenticate your Python server endpoints, you need to send the user's access token in the headers of the request to your server, and then make a request to Stack Auth's server API to verify the user identity.
Sending requests to your server endpoints
On the client side (frontend), you can retrieve the access token from the user
object by calling user.getAuthJson()
. This will return an object containing accessToken
.
Then, you can call your Python server endpoint with the access token in the headers:
// Frontend code
const { accessToken } = await user.getAuthJson();
const response = await fetch('/api/users/me', {
headers: {
'X-Stack-Access-Token': accessToken,
},
// your other options and parameters
});
Authenticating users on Python server endpoints
Stack Auth provides two methods for authenticating users on your Python server endpoints:
- JWT Verification: A fast, lightweight approach that validates the user's token locally without making external requests. Ideal for high-performance applications.
- REST API Verification: Makes a request to Stack Auth's servers to validate the token and retrieve comprehensive user information. Best when you need complete, up-to-date user data.
Using JWT
JWT verification is faster and reduces external dependencies. Install the required packages:
pip install PyJWT[crypto] requests
Here's how to implement JWT verification in your Python backend:
import jwt
from jwt import PyJWKClient
from jwt.exceptions import InvalidTokenError
# You can cache this and refresh it with a low frequency
jwks_client = PyJWKClient("https://api.stack-auth.com/api/v1/projects/<your-project-id>/.well-known/jwks.json")
def verify_jwt_token(access_token):
"""
Verify JWT token and extract user information
Returns user data or None if invalid
"""
try:
signing_key = jwks_client.get_signing_key_from_jwt(access_token)
payload = jwt.decode(
access_token,
signing_key.key,
algorithms=["ES256"],
audience="<your-project-id>",
)
return {
'user_id': payload['sub'],
'is_anonymous': payload.get('role') == 'anon'
}
except InvalidTokenError:
return None
except Exception:
return None
# Example usage
access_token = 'access token from the headers'
user_data = verify_jwt_token(access_token)
if user_data:
print(f'Authenticated user with ID: {user_data["user_id"]}')
else:
print('Invalid user')
Now you can use this JWT verification in your Python web framework. Here are examples for different frameworks:
from flask import Flask, request, jsonify
from functools import wraps
app = Flask(__name__)
def authenticate_user(request):
"""Extract and verify access token from request headers"""
access_token = request.headers.get('X-Stack-Access-Token')
if not access_token:
return None
return verify_jwt_token(access_token)
def require_auth(f):
@wraps(f)
def decorated_function(*args, **kwargs):
user = authenticate_user(request)
if not user:
return jsonify({'error': 'Unauthorized'}), 401
return f(user, *args, **kwargs)
return decorated_function
@app.route('/api/users/me')
@require_auth
def get_current_user(user):
return jsonify({
'user_id': user['user_id'],
'is_anonymous': user['is_anonymous']
})
Using the REST API
For cases where you need complete user information including email, you can use the stack_auth_request
helper function from the setup guide:
def authenticate_user_with_api(access_token):
"""
Authenticate user and get complete profile via REST API
Returns full user information including email, display name, etc.
"""
try:
user_data = stack_auth_request('GET', 'api/v1/users/me', headers={
'x-stack-access-token': access_token
})
return {
'id': user_data['id'],
'display_name': user_data['display_name'],
'primary_email': user_data['primary_email'],
'primary_email_verified': user_data['primary_email_verified'],
'profile_image_url': user_data['profile_image_url'],
'signed_up_at_millis': user_data['signed_up_at_millis'],
'last_active_at_millis': user_data['last_active_at_millis'],
'has_password': user_data['has_password'],
'is_anonymous': user_data['is_anonymous'],
'oauth_providers': user_data['oauth_providers']
}
except Exception as e:
print(f"Authentication failed: {e}")
return None
# Example usage
access_token = request.headers.get('X-Stack-Access-Token')
if access_token:
user_info = authenticate_user_with_api(access_token)
if user_info:
print(f"Authenticated user: {user_info['primary_email']}")
print(f"Display name: {user_info['display_name']}")
else:
print("Authentication failed")
Environment Configuration
As shown in the setup guide, make sure you have your Stack Auth credentials configured:
import os
stack_project_id = os.getenv("STACK_PROJECT_ID")
stack_publishable_client_key = os.getenv("STACK_PUBLISHABLE_CLIENT_KEY")
stack_secret_server_key = os.getenv("STACK_SECRET_SERVER_KEY")
And ensure you have the stack_auth_request
helper function available from the setup guide.
Error Handling Best Practices
from enum import Enum
from django.http import JsonResponse
class AuthError(Enum):
MISSING_TOKEN = "Access token required"
INVALID_TOKEN = "Invalid or expired access token"
SERVER_ERROR = "Authentication server error"
def safe_authenticate_user(request):
"""
Robust authentication with proper error handling
"""
access_token = request.headers.get('X-Stack-Access-Token')
if not access_token:
return None, AuthError.MISSING_TOKEN
try:
user_data = verify_jwt_token(access_token)
if user_data:
return user_data, None
else:
return None, AuthError.INVALID_TOKEN
except Exception as e:
print(f"Authentication error: {e}")
return None, AuthError.SERVER_ERROR
# Usage in your endpoints
def protected_endpoint(request):
user, error = safe_authenticate_user(request)
if error:
return JsonResponse({'error': error.value}, status=401)
# User is authenticated, proceed with your logic
return JsonResponse({'user': user})
Complete Backend Integration Example
Here's a comprehensive example that demonstrates both JWT and REST API authentication working together:
import os
import jwt
import requests
from jwt import PyJWKClient
from jwt.exceptions import InvalidTokenError
from enum import Enum
# Setup (from setup guide)
stack_project_id = os.getenv("STACK_PROJECT_ID")
stack_publishable_client_key = os.getenv("STACK_PUBLISHABLE_CLIENT_KEY")
stack_secret_server_key = os.getenv("STACK_SECRET_SERVER_KEY")
if not stack_project_id:
raise RuntimeError("STACK_PROJECT_ID is not set")
def stack_auth_request(method, endpoint, **kwargs):
res = requests.request(
method,
f'https://api.stack-auth.com/{endpoint}',
headers={
'x-stack-access-type': 'server',
'x-stack-project-id': stack_project_id,
'x-stack-publishable-client-key': stack_publishable_client_key,
'x-stack-secret-server-key': stack_secret_server_key,
**kwargs.pop('headers', {}),
},
timeout=10,
**kwargs,
)
if res.status_code >= 400:
raise Exception(f"Stack Auth API request failed with {res.status_code}: {res.text}")
return res.json()
# JWT verification setup
jwks_client = PyJWKClient(f"https://api.stack-auth.com/api/v1/projects/{stack_project_id}/.well-known/jwks.json")
def verify_jwt_token(access_token):
"""Fast JWT verification - returns basic user info"""
try:
signing_key = jwks_client.get_signing_key_from_jwt(access_token)
payload = jwt.decode(
access_token,
signing_key.key,
algorithms=["ES256"],
audience=stack_project_id
)
return {
'user_id': payload['sub'],
'is_anonymous': payload.get('role') == 'anon'
}
except (InvalidTokenError, Exception):
return None
def get_full_user_info(access_token):
"""REST API call - returns complete user profile"""
try:
user_data = stack_auth_request('GET', 'api/v1/users/me', headers={
'x-stack-access-token': access_token
})
return user_data
except Exception:
return None
class AuthenticationService:
@staticmethod
def authenticate_request(request, require_full_profile=False):
"""
Authenticate a request with optional full profile retrieval
Args:
request: The HTTP request object
require_full_profile: If True, fetches complete user info via REST API
Returns:
User data dictionary or None if authentication fails
"""
access_token = request.headers.get('X-Stack-Access-Token')
if not access_token:
return None
if require_full_profile:
# Use REST API for complete user information
return get_full_user_info(access_token)
else:
# Use JWT for fast authentication
return verify_jwt_token(access_token)
@staticmethod
def require_auth(require_full_profile=False):
"""Decorator for protecting endpoints"""
def decorator(func):
def wrapper(request, *args, **kwargs):
user = AuthenticationService.authenticate_request(request, require_full_profile)
if not user:
return {'error': 'Unauthorized'}, 401
return func(request, user, *args, **kwargs)
return wrapper
return decorator
# Example usage in different scenarios
@AuthenticationService.require_auth(require_full_profile=False)
def fast_protected_endpoint(request, user):
"""Fast endpoint using JWT verification"""
return {
'message': f'Hello user {user["user_id"]}!',
'is_anonymous': user['is_anonymous']
}
@AuthenticationService.require_auth(require_full_profile=True)
def profile_endpoint(request, user):
"""Endpoint that needs complete user info"""
return {
'user_id': user['id'],
'display_name': user['display_name'],
'email': user['primary_email'],
'email_verified': user['primary_email_verified'],
'profile_image': user['profile_image_url'],
'is_anonymous': user['is_anonymous']
}
# Error handling example
class AuthError(Enum):
MISSING_TOKEN = "Access token required"
INVALID_TOKEN = "Invalid or expired access token"
SERVER_ERROR = "Authentication server error"
def safe_authenticate(request, require_full_profile=False):
"""Authentication with comprehensive error handling"""
access_token = request.headers.get('X-Stack-Access-Token')
if not access_token:
return None, AuthError.MISSING_TOKEN
try:
if require_full_profile:
user_data = get_full_user_info(access_token)
else:
user_data = verify_jwt_token(access_token)
if user_data:
return user_data, None
else:
return None, AuthError.INVALID_TOKEN
except Exception as e:
print(f"Authentication error: {e}")
return None, AuthError.SERVER_ERROR
Performance Considerations
- JWT Verification: Faster, no external requests, but limited user data (only
user_id
andis_anonymous
) - REST API Verification: Slower, requires network calls, but provides complete user information including email, profile, etc.
- Hybrid Approach: Use JWT for basic authentication, then fetch full profile only when needed
- Caching: Consider caching JWKs and user data for better performance
- Connection Pooling: Use session objects for REST API calls to reuse connections
Choose the appropriate method based on your endpoint's requirements:
- Use JWT for high-performance endpoints that only need user ID
- Use REST API when you need complete user profiles, email verification status, etc.
- Use hybrid approach to optimize performance while maintaining flexibility