Files
MyFastHtml/src/myfasthtml/auth

FastHTML + FastAPI Authentication Integration

This package provides a complete authentication system integrating FastHTML (frontend) with FastAPI (backend).

Architecture

auth/
├── __init__.py              # Package initialization
├── utils.py                 # JWT helpers + Beforeware
├── pages/
│   ├── __init__.py
│   ├── LoginPage.py         # Login form component
│   ├── RegisterPage.py      # Registration form component
│   └── WelcomePage.py       # Protected welcome page
└── routes.py                # All routes (public + protected)

Features

  • Login page with email/password authentication
  • Registration page with password confirmation
  • Automatic token refresh (proactive refresh at 5 minutes before expiry)
  • Route protection with Beforeware (whitelist support)
  • Session management using FastHTML signed cookies
  • DaisyUI 5 components for beautiful UI
  • HTMX integration for dynamic form submissions

Dependencies

Install the required packages:

pip install fasthtml httpx python-jose[cryptography] --break-system-packages

Note: python-jose may already be installed if you have FastAPI.

Configuration

1. Update API configuration in auth/utils.py

API_BASE_URL = "http://localhost:5003"  # Your FastAPI backend URL
JWT_SECRET = "jwt-secret-to-change"     # Must match your FastAPI secret

2. Create your main.py

from fasthtml.common import fast_app, serve, Link, Script
from auth import create_auth_beforeware, setup_auth_routes

# Import your existing FastAPI auth app
# from your_auth_module import create_auth_app_for_sqlite

# Create Beforeware for route protection
beforeware = create_auth_beforeware()

# Create FastHTML app
app, rt = fast_app(
    before=beforeware,
    hdrs=(
        # DaisyUI 5 CSS
        Link(rel='stylesheet', href='https://cdn.jsdelivr.net/npm/daisyui@5/daisyui.css'),
        # Tailwind CSS 4
        Script(src='https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4'),
    )
)

# Mount FastAPI auth backend
# auth_api = create_auth_app_for_sqlite("Users.db", "jwt-secret-to-change")
# app.mount("/auth", auth_api)

# Setup authentication routes
setup_auth_routes(app, rt)

if __name__ == "__main__":
    serve()

Usage

Starting the application

python main.py

The application will be available at http://localhost:5001.

Routes

Public routes:

  • GET /login - Display login page
  • POST /login - Handle login submission
  • GET /register - Display registration page
  • POST /register - Handle registration submission

Protected routes (require authentication):

  • GET / - Welcome page (home)
  • GET /welcome - Alternative welcome page
  • POST /logout - Logout and revoke tokens

Adding custom protected routes

@rt("/dashboard")
def get_dashboard(session, auth):
    """
    Custom protected route.
    
    The 'auth' parameter is automatically injected by the Beforeware.
    """
    user_info = session.get('user_info', {})
    return Div(
        H1(f"Dashboard for {user_info.get('email', 'User')}"),
        # Your dashboard content
    )

Extending the whitelist

If you need to add more public routes:

beforeware = create_auth_beforeware(
    additional_patterns=[
        '/about',
        '/contact',
        r'/api/.*',  # Regex patterns are supported
    ]
)

How it works

1. Beforeware Protection

The auth_before() function runs before every route (except whitelisted ones):

  1. Checks if access_token exists in session
  2. Validates token expiration
  3. If token expires in < 5 minutes, automatically refreshes it
  4. If refresh fails or no token exists, redirects to /login

2. Token Refresh Flow

User Request → Beforeware checks token
                ↓
         Token expires in < 5 min?
                ↓ Yes
         Call /auth/refresh with refresh_token
                ↓
         Update session with new tokens
                ↓
         Continue to route handler

3. Session Storage

Session contains:

  • access_token (JWT, 30 minutes validity)
  • refresh_token (Opaque, 7 days validity)
  • user_info (email, id, etc.)

Customization

Changing the refresh threshold

Edit auth/utils.py:

TOKEN_REFRESH_THRESHOLD_MINUTES = 5  # Change to your preferred value

Customizing page components

Each page component is a class with:

  • __init__() - Constructor with parameters
  • _render() - HTML generation logic
  • __ft__() - FastHTML protocol method

Example:

class LoginPage:
    def __init__(self, error_message=None, redirect_url="/"):
        self.error_message = error_message
        self.redirect_url = redirect_url
    
    def _render(self):
        # Build your custom HTML here
        return Div(...)
    
    def __ft__(self):
        return self._render()

Styling with DaisyUI 5

DaisyUI 5 provides semantic class names:

# Button
Button("Click me", cls="btn btn-primary")

# Input
Input(type="email", cls="input input-bordered")

# Card
Div(
    Div(
        H2("Title", cls="card-title"),
        P("Content"),
        cls="card-body"
    ),
    cls="card bg-base-100 shadow-xl"
)

# Alert
Div(
    Span("Success message"),
    cls="alert alert-success"
)

Troubleshooting

Issue: "Module not found: auth"

Make sure the auth directory is in your Python path or in the same directory as main.py.

Issue: "Connection refused" when calling API

Check that:

  1. API_BASE_URL in auth/utils.py matches your FastAPI server
  2. Your FastAPI backend is running
  3. The /auth endpoints are correctly mounted

Issue: Token refresh not working

Verify:

  1. JWT_SECRET in auth/utils.py matches your FastAPI secret
  2. Your FastAPI /auth/refresh endpoint is working
  3. The refresh token is being stored correctly in the session

Issue: Infinite redirect loop

Check:

  1. The /login route is in the whitelist (it should be by default)
  2. Your FastAPI /auth/login endpoint returns access_token and refresh_token

Testing

To test the authentication flow:

  1. Start your application
  2. Navigate to http://localhost:5001/
  3. You should be redirected to /login
  4. Register a new account at /register
  5. Login with your credentials
  6. You should see the welcome page
  7. Your session will automatically refresh tokens as needed

API Integration

This package expects your FastAPI backend to provide these endpoints:

  • POST /auth/login - Returns access_token and refresh_token
  • POST /auth/register - Creates a new user
  • POST /auth/refresh - Refreshes the access token
  • GET /auth/me - Returns current user info (requires Bearer token)
  • POST /auth/logout - Revokes refresh token

License

This code is provided as-is for educational and development purposes.

Questions?

Feel free to modify and extend this authentication system according to your needs!