Skip to main content
Prerequisites: Before you begin, ensure you have the following installed:
  • Python 3.9 or newer
  • pip 20.0 or newer
  • jq - Required for Auth0 CLI setup
Flask Version Compatibility: This quickstart uses Flask 2.0+ with the [async] extra for async support.

Get Started

This quickstart demonstrates how to add Auth0 authentication to a Flask application. You’ll build a secure web application with login functionality, protected routes, and user profile access using the Auth0 WebApp Python SDK.
1

Set up your environment

Create a new directory for your Flask project:
mkdir auth0-flask-app && cd auth0-flask-app
Create a virtual environment:
python -m venv venv
source venv/bin/activate  # On Windows: venv\Scripts\activate
2

Install dependencies

Create requirements.txt to track your dependencies:
requirements.txt
auth0-server-python>=1.0.0b7
flask[async]>=2.0.0
python-dotenv>=1.0.0
The requirements.txt file tracks all project dependencies. To install them, run:
pip install -r requirements.txt
3

Setup your Auth0 App

Next up, you need to create a new app on your Auth0 tenant and add the environment variables to your project.You can choose to do this automatically by running a CLI command or do it manually via the Dashboard:
Run the following shell command on your project’s root directory to create an Auth0 application and generate a .env file:
AUTH0_APP_NAME="My Flask App" && brew tap auth0/auth0-cli && brew install auth0 && auth0 login --no-input && auth0 apps create -n "${AUTH0_APP_NAME}" -t regular -c http://localhost:5000/callback -l http://localhost:5000 -o http://localhost:5000 --reveal-secrets --json > app-details.json && CLIENT_ID=$(python3 -c "import json; print(json.load(open('app-details.json'))['client_id'])") && CLIENT_SECRET=$(python3 -c "import json; print(json.load(open('app-details.json'))['client_secret'])") && DOMAIN=$(auth0 tenants list --json | python3 -c "import sys, json; print([t['name'] for t in json.load(sys.stdin) if t.get('active')][0])") && SECRET=$(openssl rand -hex 64) && echo "AUTH0_DOMAIN=${DOMAIN}" > .env && echo "AUTH0_CLIENT_ID=${CLIENT_ID}" >> .env && echo "AUTH0_CLIENT_SECRET=${CLIENT_SECRET}" >> .env && echo "AUTH0_SECRET=${SECRET}" >> .env && echo "AUTH0_REDIRECT_URI=http://localhost:5000/callback" >> .env && rm app-details.json && echo ".env file created with your Auth0 details:" && cat .env
4

Create Auth Configuration, Routes, and Templates

Create files
mkdir templates static && touch app.py auth.py templates/index.html templates/profile.html static/style.css
And add the following code snippets:
Development Only: This example uses simple in-memory storage classes (MemoryStateStore and MemoryTransactionStore) for demonstration purposes. These stores will lose all session data when your application restarts and won’t work in multi-instance deployments.For production applications, you must implement persistent storage. The SDK is framework-agnostic and requires you to provide custom StateStore and TransactionStore implementations. See the official SDK storage examples for detailed guidance on implementing Redis, PostgreSQL, or other persistent storage backends.
5

Run your app

Start the Flask development server:
python app.py
Your app will be available at http://localhost:5000. The Auth0 SDK handles authentication routes automatically.
CheckpointYou should now have a fully functional Auth0 login page running on your localhost

Advanced Usage

If you need to call a protected API, retrieve an access token:
@app.route('/api-call')
@require_auth
async def api_call():
    try:
        # Get access token for your API
        access_token = await auth0.get_access_token(
            audience='https://your-api.example.com',
            store_options=g.store_options
        )
        
        # Use the token to call your API
        # headers = {'Authorization': f'Bearer {access_token}'}
        # response = requests.get('https://your-api.example.com/data', headers=headers)
        
        return f"Access token retrieved: {access_token[:20]}..."
    except Exception as e:
        return f"Error getting access token: {str(e)}", 500
To use this feature, you must:
  1. Set AUTH0_AUDIENCE in your .env file
  2. Include offline_access in your scopes (for refresh tokens)
  3. Update authorization_params in auth.py:
    authorization_params={
        'scope': 'openid profile email offline_access',
        'audience': os.getenv('AUTH0_AUDIENCE')
    }
    
By default, the SDK uses cookie-based storage. For production environments with specific needs (horizontal scaling, session sharing across services), you can configure custom storage backends like Redis or PostgreSQL.When to use custom storage:
  • You need to share sessions across multiple servers
  • You have large session data exceeding cookie size limits
  • You need centralized session management for backchannel logout
Redis Example:
auth.py
from auth0_server_python.stores.redis_state_store import RedisStateStore
import redis.asyncio as redis

# Initialize custom state store
redis_client = redis.from_url(os.getenv('REDIS_URL', 'redis://localhost:6379'))
state_store = RedisStateStore(
    secret=os.getenv('AUTH0_SECRET'),
    redis_client=redis_client
)

# Pass to ServerClient
auth0 = ServerClient(
    domain=os.getenv('AUTH0_DOMAIN'),
    client_id=os.getenv('AUTH0_CLIENT_ID'),
    client_secret=os.getenv('AUTH0_CLIENT_SECRET'),
    secret=os.getenv('AUTH0_SECRET'),
    redirect_uri=os.getenv('AUTH0_REDIRECT_URI'),
    state_store=state_store,  # Custom storage
    authorization_params={'scope': 'openid profile email'}
)
For most applications, the default cookie-based storage is sufficient. Custom storage requires implementing the StateStore interface. See the SDK examples for detailed implementations.

Common Issues

Problem: You see “MissingRequiredArgumentError: secret” when starting the appCause: The AUTH0_SECRET environment variable is missing or not loaded properly.Solution:
  1. Verify your .env file exists in the project root
  2. Ensure python-dotenv is installed: pip install python-dotenv
  3. Generate a new secret if needed: openssl rand -hex 64
  4. Add it to .env: AUTH0_SECRET=your_generated_secret
  5. Restart your Flask application
Problem: You see “Callback URL mismatch” or “invalid_request” error during loginCause: The redirect URI in your code doesn’t match what’s registered in Auth0 Dashboard.Solution:
  1. Check your .env file: AUTH0_REDIRECT_URI=http://localhost:5000/callback
  2. Go to Auth0 Dashboard → Applications → Your App → Settings
  3. Add http://localhost:5000/callback to Allowed Callback URLs
  4. Click Save Changes
  5. Restart your Flask application
Problem: You see “RuntimeError: This event loop is already running” or similar async errorsCause: Flask 2.0+ async support may have issues with certain configurations.Solution:Install Flask with async support:
pip install "flask[async]"
Then restart your Flask application.
Problem: You see “ModuleNotFoundError: No module named ‘auth0_server_python’” or similarCause: The SDK is not installed or the virtual environment is not activated.Solution:
  1. Ensure your virtual environment is activated:
    source venv/bin/activate  # macOS/Linux
    # or
    venv\Scripts\activate  # Windows
    
  2. Install the SDK:
    pip install auth0-server-python "flask[async]" python-dotenv
    
  3. Verify installation:
pip list | grep auth0
Problem: You see “ClaimDecodingFailed” or “Failed to decode claims” error during authenticationCause: The ID token or access token received from Auth0 couldn’t be properly decoded, often due to:
  • Invalid JWT format
  • Corrupted session data
  • Mismatched signing algorithms
  • Clock skew between your server and Auth0
Solution:
  1. Ensure your AUTH0_CLIENT_SECRET is correct in the .env file
  2. Check your system time is synchronized (NTP):
    # macOS
    sudo sntp -sS time.apple.com
    
    # Linux
    sudo ntpdate -s time.nist.gov
    
  3. Clear browser cookies and restart authentication
  4. Verify the AUTH0_DOMAIN doesn’t include https:// prefix
  5. Check Auth0 Dashboard → Applications → Your App → Settings → Advanced → OAuth → JsonWebToken Signature Algorithm matches your SDK configuration
Problem: You see “Token has expired” or “invalid_token” errorsCause: The access token or ID token has exceeded its lifetime, or the session has expired.Solution:
  1. The SDK automatically handles token refresh if you include offline_access scope:
    authorization_params={
        'scope': 'openid profile email offline_access',
    }
    
  2. For APIs, ensure you’re requesting fresh tokens:
    access_token = await auth0.get_access_token(
        audience='https://your-api.example.com',
        force_refresh=True  # Force refresh if needed
    )
    
  3. Adjust token lifetimes in Auth0 Dashboard → Applications → Your App → Settings → Advanced → OAuth
  4. Implement proper error handling to redirect users to login when tokens expire
Problem: You see CORS errors or “Blocked by CORS policy” in browser consoleCause: Your application’s origin isn’t properly configured in Auth0.Solution:
  1. Add your origin to Auth0 Dashboard → Applications → Your App → Settings:
    • Allowed Web Origins: http://localhost:5000
    • Allowed Callback URLs: http://localhost:5000/callback
    • Allowed Logout URLs: http://localhost:5000
  2. For production, add your production URLs:
    https://yourdomain.com
    https://yourdomain.com/callback
    
  3. Ensure your Flask app has proper CORS configuration if calling Auth0 APIs from frontend JavaScript:
    from flask_cors import CORS
    CORS(app, origins=['http://localhost:5000'])
    
Problem: You see “Too many requests” or “Rate limit exceeded” errorsCause: Your application has exceeded Auth0’s rate limits for authentication requests.Solution:
  1. Check Auth0 Dashboard → Monitoring → Logs for rate limit details
  2. Implement exponential backoff for retries:
    import time
    from auth0_server_python.error import ApiError
    
    async def login_with_retry():
        max_retries = 3
        for attempt in range(max_retries):
            try:
                return await auth0.start_interactive_login({}, g.store_options)
            except ApiError as e:
                if e.status_code == 429 and attempt < max_retries - 1:
                    wait_time = 2 ** attempt
                    time.sleep(wait_time)
                else:
                    raise
    
  3. Review your Auth0 subscription plan limits
  4. Optimize authentication flow to reduce unnecessary token requests
  5. Cache tokens appropriately instead of requesting new ones frequently