Stack Auth
GuidesSDKComponentsAPI Reference
Back to home
Platform
OverviewFAQ
Getting Started
Setup
Concepts
Backend IntegrationUser AuthenticationTeams ManagementJWT Tokens
Other
Self-hostCLI Authentication
Stack Auth Docs

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:

  1. JWT Verification: A fast, lightweight approach that validates the user's token locally without making external requests. Ideal for high-performance applications.
  2. 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 and is_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
Previous PageSetup
Next PageUser Authentication

Table of Contents

Navigate this page

On this page

Sending requests to your server endpointsAuthenticating users on Python server endpointsUsing JWTUsing the REST APIEnvironment ConfigurationError Handling Best PracticesComplete Backend Integration ExamplePerformance Considerations

Stack Auth AI

Documentation assistant

Experimental: AI responses may not always be accurate—please verify important details.

For the most accurate information, please join our Discord or email us.

How can I help?

Ask me about Stack Auth while you browse the docs.