# 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!