# 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:
```bash
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`
```python
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`
```python
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
```bash
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
```python
@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:
```python
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`:
```python
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:
```python
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:
```python
# 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!