code-forge (the stdio MCP server) calls the REST API directly and has
no login flow of its own. With auth disabled it already passes
through as guest, same as before - but the moment CLAUDE_OS_EMAIL/
PASSWORD get set, it would start getting 401'd with no way to
authenticate.
get_current_user now accepts a CLAUDE_OS_SERVICE_TOKEN as a bearer
token, checked with a constant-time compare, alongside normal user
JWTs. claude_code_mcp.py sends it (via a shared _auth_headers() used
by all 5 of its httpx.AsyncClient() call sites) whenever
CLAUDE_OS_SERVICE_TOKEN is set, and sends nothing when it isn't - a
no-op change for anyone not using auth.
Since this process can be launched directly (e.g. over SSH as a stdio
server) without claude-os's own .env sourced into its shell or its cwd
set to the project root, it now loads .env explicitly by path relative
to the script's own location rather than relying on load_dotenv()'s
cwd-relative default search.
Two gaps found while wiring auth into the data routes:
1. mcp_server/auth.py: HTTPBearer() defaults to auto_error=True, which
rejects any request with no Authorization header before
get_current_user's own "auth not configured -> allow as guest"
check ever runs. That meant turning on Depends(get_current_user)
anywhere 401'd every request even with auth fully disabled.
Switched to auto_error=False and handle the missing-credentials
case explicitly inside get_current_user instead.
2. Frontend never actually sent the stored token on data requests.
auth.tsx only attached it to its own /api/auth/me check; api.ts's
fetch() calls (chat, stats, documents, upload, delete) and every
axios call elsewhere in the app (projects, kanban, hooks) had no
Authorization header at all. Added an authHeaders() helper in
api.ts, and a single axios request interceptor in auth.tsx that
attaches the token app-wide since every component imports the same
default axios instance.
Known gap not addressed here: mcp_server/claude_code_mcp.py (the
code-forge stdio MCP server) calls the REST API with no token at all.
Harmless while auth is disabled (current state), but it will start
getting 401'd the moment CLAUDE_OS_EMAIL/PASSWORD are actually set -
needs a service-token bypass if that's ever turned on.
Implemented optional authentication to keep strangers out of the
Claude OS frontend when deployed to production servers.
Backend (FastAPI):
- mcp_server/auth.py: JWT-based authentication
* Bcrypt password hashing
* Environment-based user credentials (no database needed)
* 7-day token expiration
* Optional - disabled by default
- mcp_server/server.py: Auth endpoints
* POST /api/auth/login - Returns JWT token
* GET /api/auth/me - Get current user
* GET /api/auth/status - Check if auth enabled
- requirements.txt: Added dependencies
* passlib[bcrypt] for password hashing
* python-jose[cryptography] for JWT
* python-multipart for form data
Frontend (React):
- frontend/src/lib/auth.tsx: Auth context and hooks
* Manages authentication state
* Automatic token validation
* localStorage for token persistence
- frontend/src/pages/Login.tsx: Beautiful login page
* Gradient design matching Claude OS branding
* Clear error messages
* Loading states
- frontend/src/components/ProtectedRoute.tsx: Route protection
* Automatically redirects to login if not authenticated
* Shows loading state during auth check
* Bypasses auth if disabled
- frontend/src/App.tsx: Integrated auth
* Wrapped app in AuthProvider
* Protected routes for Welcome and MainApp
* Login route for authentication
Documentation:
- AUTH_SETUP.md: Complete authentication guide
* Quick start instructions
* Password hash generation
* Production deployment steps
* Troubleshooting guide
- .env.example: Added auth configuration
* CLAUDE_OS_EMAIL (login email)
* CLAUDE_OS_PASSWORD (dev - plain text)
* CLAUDE_OS_PASSWORD_HASH (production - bcrypt hash)
* CLAUDE_OS_SECRET_KEY (JWT secret)
- docs/PRODUCTION_DEPLOYMENT.md: Added Step 5
* Authentication setup for production
* Hash and secret generation commands
* Testing instructions
Features:
✅ Optional authentication (disabled by default)
✅ Single user via environment variables
✅ Secure bcrypt hashing
✅ JWT tokens with 7-day expiration
✅ Beautiful login UI
✅ No database required for user management
Usage:
1. Set CLAUDE_OS_EMAIL and CLAUDE_OS_PASSWORD in .env
2. Restart Claude OS
3. Visit /login to authenticate
4. Token valid for 7 days
To disable auth: Remove CLAUDE_OS_EMAIL from .env
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>