Authentication
SideSeat uses a token-based authentication system that secures the web UI while remaining easy to use for local development.
How It Works
Section titled “How It Works”- Server starts → Generates a bootstrap token and prints URL to terminal
- User clicks URL → Browser exchanges bootstrap token for JWT session
- JWT stored in cookie → HttpOnly, SameSite=Strict cookie for security
- Protected routes → API endpoints validate JWT on each request
Quick Start
Section titled “Quick Start”# Start with authentication (default)sideseat start
# Output:# SideSeat v1.0.4# ➜ Local: http://127.0.0.1:5001/ui?token=abc123...Click the URL with the token to authenticate automatically.
Disabling Authentication
Section titled “Disabling Authentication”For development or trusted environments, authentication can be disabled:
# CLI flagsideseat start --no-auth
# Environment variableSIDESEAT_AUTH_ENABLED=false sideseat startOr in configuration file:
{ "auth": { "enabled": false }}Authentication Flow
Section titled “Authentication Flow”Bootstrap Token Exchange
Section titled “Bootstrap Token Exchange”Terminal URL: http://localhost:5001/ui?token=<bootstrap_token> ↓Frontend: POST /api/v1/auth/exchange { token: "<bootstrap_token>" } ↓Server: Validates token, returns JWT in Set-Cookie header ↓Browser: Stores HttpOnly cookie, redirects to appSession Validation
Section titled “Session Validation”Browser: GET /api/v1/protected Cookie: sideseat_session=<jwt> ↓Server: Validates JWT signature and expiration ↓Response: 200 OK (valid) or 401 Unauthorized (invalid/expired)API Endpoints
Section titled “API Endpoints”POST /api/v1/auth/exchange
Section titled “POST /api/v1/auth/exchange”Exchange a bootstrap token for a JWT session.
Request:
{ "token": "bootstrap_token_here" }Success Response (200):
{ "success": true, "message": "Authentication successful" }Plus Set-Cookie: sideseat_session=<jwt>; HttpOnly; SameSite=Strict; Path=/api
Error Response (401):
{ "error": "unauthorized", "code": "BOOTSTRAP_INVALID", "message": "Invalid bootstrap token"}GET /api/v1/auth/status
Section titled “GET /api/v1/auth/status”Check current authentication status.
Response:
{ "authenticated": true, "auth_method": "bootstrap", "expires_at": "2025-02-24T12:00:00Z"}POST /api/v1/auth/logout
Section titled “POST /api/v1/auth/logout”Clear the session cookie.
Response:
{ "success": true, "message": "Logged out successfully" }Security Features
Section titled “Security Features”| Feature | Implementation |
|---|---|
| Token Storage | JWT signing key in OS keychain via SecretManager |
| Cookie Security | HttpOnly, SameSite=Strict, Path=/api |
| Token Validation | Constant-time comparison (timing attack resistant) |
| Session Expiry | 30-day JWT lifetime |
| CSRF Protection | SameSite cookie + Origin header validation |
JWT Claims
Section titled “JWT Claims”{ "sub": "local", "iat": 1703001234, "exp": 1705593234, "jti": "unique-uuid-v4", "auth_method": "bootstrap"}| Claim | Description |
|---|---|
sub | Subject identifier (“local” for bootstrap auth) |
iat | Issued at timestamp |
exp | Expiration timestamp (30 days from issue) |
jti | Unique JWT ID for tracking |
auth_method | Authentication method used |
Error Responses
Section titled “Error Responses”All authentication errors return 401 with a structured response:
{ "error": "unauthorized", "code": "ERROR_CODE", "message": "Human-readable message"}| Code | Description |
|---|---|
AUTH_REQUIRED | No session cookie present |
TOKEN_EXPIRED | JWT has expired |
TOKEN_INVALID | Invalid JWT signature |
BOOTSTRAP_INVALID | Invalid bootstrap token |
ORIGIN_NOT_ALLOWED | Request from disallowed origin |
Protecting API Routes
Section titled “Protecting API Routes”use sideseat::auth::{require_auth, AuthManager};use axum::{Router, middleware, routing::get};
let auth_manager = Arc::new(AuthManager::init(&secrets, true).await?);
let protected_routes = Router::new() .route("/protected", get(handler)) .layer(middleware::from_fn_with_state( auth_manager.clone(), require_auth ));Frontend Integration
Section titled “Frontend Integration”React Auth Context
Section titled “React Auth Context”import { useAuth } from "@/auth/context";
function MyComponent() { const { authenticated, loading, login, logout } = useAuth();
if (loading) return <div>Loading...</div>; if (!authenticated) return <div>Please authenticate</div>;
return <div>Welcome!</div>;}Protected Routes
Section titled “Protected Routes”import { AuthGuard } from "@/auth/guard";
<AuthGuard> <ProtectedPage /></AuthGuard>API Client
Section titled “API Client”import { fetchAPI, AuthError } from "@/api/client";
try { const data = await fetchAPI("/protected");} catch (e) { if (e instanceof AuthError) { // Redirect to login }}Best Practices
Section titled “Best Practices”- Always use HTTPS in production - Cookies are sent in clear text over HTTP
- Click “Always Allow” - On macOS, grant keychain access permanently
- Don’t share terminal URLs - Bootstrap tokens grant access
- Monitor session expiry - 30-day tokens should be refreshed periodically
- Use —no-auth carefully - Only in trusted, isolated environments