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 pagePOST /login- Handle login submissionGET /register- Display registration pagePOST /register- Handle registration submission
Protected routes (require authentication):
GET /- Welcome page (home)GET /welcome- Alternative welcome pagePOST /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):
- Checks if
access_tokenexists in session - Validates token expiration
- If token expires in < 5 minutes, automatically refreshes it
- 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:
API_BASE_URLinauth/utils.pymatches your FastAPI server- Your FastAPI backend is running
- The
/authendpoints are correctly mounted
Issue: Token refresh not working
Verify:
JWT_SECRETinauth/utils.pymatches your FastAPI secret- Your FastAPI
/auth/refreshendpoint is working - The refresh token is being stored correctly in the session
Issue: Infinite redirect loop
Check:
- The
/loginroute is in the whitelist (it should be by default) - Your FastAPI
/auth/loginendpoint returnsaccess_tokenandrefresh_token
Testing
To test the authentication flow:
- Start your application
- Navigate to
http://localhost:5001/ - You should be redirected to
/login - Register a new account at
/register - Login with your credentials
- You should see the welcome page
- Your session will automatically refresh tokens as needed
API Integration
This package expects your FastAPI backend to provide these endpoints:
POST /auth/login- Returnsaccess_tokenandrefresh_tokenPOST /auth/register- Creates a new userPOST /auth/refresh- Refreshes the access tokenGET /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!