---
title: Authentication
---

This guide covers how to implement authentication in your Python application using Stack Auth. We'll walk through all the available authentication methods and show you how to handle user sessions.

## Overview

Stack Auth supports multiple authentication methods that you can enable or disable based on your needs:

- **[Email/Password](./password.mdx)** - Traditional email and password authentication
- **[Magic Links (OTP)](./magic-links.mdx)** - Passwordless authentication via email
- **[OAuth Providers](./oauth.mdx)** - Sign in with Google, GitHub, Facebook, and more
- **[Passkeys](./passkeys.mdx)** - Modern biometric authentication
- **[Anonymous Users](./anonymous.mdx)** - Guest users that can be upgraded later

## Basic Authentication Flow

All authentication methods in Stack Auth follow a similar pattern:

1. **Initiate authentication** - Start the sign-in/sign-up process
2. **User verification** - User completes authentication (password, email link, OAuth, etc.)
3. **Receive tokens** - Get access and refresh tokens
4. **Manage session** - Use tokens to authenticate API requests

Here's the basic structure for handling authentication:

```python
from typing import Optional
import requests

class AuthManager:
    def __init__(self, stack_auth_client):
        self.client = stack_auth_client
        self.current_user_token: Optional[str] = None
    
    def authenticate_request(self, access_token: str) -> dict:
        """Verify and get user info from access token"""
        return self.client._make_request(
            'GET', 
            '/api/v1/users/me',
            access_type="client",
            access_token=access_token
        )
    
    def refresh_token(self, refresh_token: str) -> dict:
        """Refresh an expired access token"""
        return self.client._make_request(
            'POST',
            '/api/v1/auth/sessions/current/refresh',
            access_type="client",
            json={'refresh_token': refresh_token}
        )
    
    def sign_out(self, access_token: str) -> bool:
        """Sign out the current user"""
        try:
            self.client._make_request(
                'DELETE',
                '/api/v1/auth/sessions/current',
                access_type="client",
                access_token=access_token
            )
            return True
        except Exception:
            return False

# Initialize auth manager
auth_manager = AuthManager(stack_auth)
```

## Token Management

Stack Auth uses two types of tokens:

- **Access Token** - Short-lived token for API requests (typically 1 hour)
- **Refresh Token** - Long-lived token to get new access tokens (typically 30 days)

### Storing Tokens Securely

For web applications, store tokens securely:

```python
from flask import session
import jwt
from datetime import datetime

def store_tokens_securely(access_token: str, refresh_token: str):
    """Store tokens in secure HTTP-only cookies or session"""
    # For session storage
    session['access_token'] = access_token
    session['refresh_token'] = refresh_token
    
    # For cookie storage (recommended for web apps)
    # Set secure, HTTP-only cookies
    response.set_cookie(
        'access_token', 
        access_token, 
        httponly=True, 
        secure=True,
        samesite='Strict'
    )

def get_current_user_from_token(access_token: str) -> Optional[dict]:
    """Extract user info from access token"""
    try:
        # Verify token and get user
        user_info = auth_manager.authenticate_request(access_token)
        return user_info
    except Exception:
        return None
```

## Middleware for Authentication

Here's how to create middleware that automatically handles authentication:

```python
from functools import wraps
from flask import request, jsonify, g

def require_auth(f):
    """Decorator that requires authentication"""
    @wraps(f)
    def decorated_function(*args, **kwargs):
        # Get token from header or cookie
        access_token = request.headers.get('Authorization')
        if access_token and access_token.startswith('Bearer '):
            access_token = access_token[7:]
        elif 'access_token' in request.cookies:
            access_token = request.cookies['access_token']
        else:
            return jsonify({'error': 'Authentication required'}), 401
        
        # Verify token and get user
        try:
            user_info = auth_manager.authenticate_request(access_token)
            g.current_user = user_info
            g.access_token = access_token
            return f(*args, **kwargs)
        except Exception as e:
            return jsonify({'error': 'Invalid token'}), 401
    
    return decorated_function

def optional_auth(f):
    """Decorator for optional authentication"""
    @wraps(f)
    def decorated_function(*args, **kwargs):
        access_token = request.headers.get('Authorization')
        if access_token and access_token.startswith('Bearer '):
            access_token = access_token[7:]
            try:
                user_info = auth_manager.authenticate_request(access_token)
                g.current_user = user_info
                g.access_token = access_token
            except Exception:
                g.current_user = None
        else:
            g.current_user = None
        
        return f(*args, **kwargs)
    
    return decorated_function
```

## Usage Examples

### Protected Route

```python
from flask import Flask, g, jsonify

app = Flask(__name__)

@app.route('/profile')
@require_auth
def get_profile():
    """Get current user's profile - requires authentication"""
    return jsonify({
        'user': g.current_user,
        'message': 'This is your profile'
    })

@app.route('/public')
@optional_auth  
def public_endpoint():
    """Public endpoint with optional user context"""
    if g.current_user:
        return jsonify({
            'message': f'Hello {g.current_user.get("display_name", "User")}!'
        })
    else:
        return jsonify({'message': 'Hello anonymous user!'})
```

### Manual Token Refresh

```python
def refresh_user_session(refresh_token: str) -> Optional[dict]:
    """Refresh user session and return new tokens"""
    try:
        token_response = auth_manager.refresh_token(refresh_token)
        return {
            'access_token': token_response['access_token'],
            'refresh_token': token_response['refresh_token']
        }
    except Exception as e:
        print(f"Token refresh failed: {e}")
        return None
```

## Error Handling

Common authentication errors and how to handle them:

```python
class AuthError(Exception):
    def __init__(self, message: str, status_code: int = 401):
        self.message = message
        self.status_code = status_code
        super().__init__(self.message)

def handle_auth_errors(func):
    """Decorator to handle common authentication errors"""
    @wraps(func)
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except requests.exceptions.HTTPError as e:
            if e.response.status_code == 401:
                raise AuthError("Invalid or expired token", 401)
            elif e.response.status_code == 403:
                raise AuthError("Insufficient permissions", 403)
            else:
                raise AuthError("Authentication error", e.response.status_code)
        except Exception as e:
            raise AuthError(f"Unexpected error: {str(e)}", 500)
    return wrapper
```

## Next Steps

Choose the authentication method that best fits your application:

1. **[Email/Password Authentication](./password.mdx)** - Most common, familiar to users
2. **[Magic Link Authentication](./magic-links.mdx)** - Modern passwordless approach  
3. **[OAuth Authentication](./oauth.mdx)** - Easy sign-in with existing accounts
4. **[Passkey Authentication](./passkeys.mdx)** - Most secure, future-proof option
5. **[Anonymous Authentication](./anonymous.mdx)** - Great for guest users

You can also enable multiple methods simultaneously to give users options.
