# Forward Auth TOTP A lightweight forward authentication service for Caddy (or any reverse proxy) that uses TOTP (Time-based One-Time Password) tokens for user authentication. ## Features - 🔐 TOTP-based authentication (compatible with Google Authenticator, Authy, 1Password, etc.) - ðŸŽŦ JWT session management with configurable duration - ðŸ‘Ĩ Support for multiple users (1-20+ TOTP seeds) - ðŸŠķ Lightweight SQLite database - 🚀 Single binary deployment - 🔄 Works with Caddy's `forward_auth` directive - ðŸ“ą Mobile-friendly login page ## Prerequisites - Go 1.21 or higher - SQLite3 - Caddy v2 (or any reverse proxy that supports forward authentication) ## Installation ### Clone the repository ```bash git clone https://github.com/yourusername/forward-auth-totp.git cd forward-auth-totp ``` ### Install dependencies ```bash go mod download ``` ### Build the application ```bash go build -o forward-auth main.go ``` ## Configuration ### Environment Variables | Variable | Description | Default | Required | |----------|-------------|---------|----------| | `JWT_SECRET` | Secret key for signing JWT tokens | (insecure default) | **Recommended** | **Important:** Always set a secure `JWT_SECRET` in production: ```bash export JWT_SECRET="your-very-secure-random-secret-here-min-32-chars" ``` Generate a secure secret: ```bash openssl rand -base64 32 ``` ### Application Constants You can modify these constants in `main.go`: - `sessionDuration`: JWT session duration (default: 24 hours) - `dbFile`: SQLite database file path (default: `auth.db`) - `jwtCookie`: Cookie name for JWT token (default: `auth_token`) ## Usage ### First Run On first run, the application will automatically generate a TOTP seed: ```bash ./forward-auth ``` Output: ``` No seeds found, generating one... New TOTP seed generated: Secret: JBSWY3DPEHPK3PXP OTPAuth URL: otpauth://totp/ForwardAuthApp:user?secret=JBSWY3DPEHPK3PXP&issuer=ForwardAuthApp Use this to set up your authenticator app. Starting auth server on :3000 ``` Scan the QR code (use the OTPAuth URL with a QR generator) or manually enter the secret into your authenticator app. ### Generate Additional TOTP Seeds For multiple users, generate additional seeds: ```bash ./forward-auth -generate ``` Each seed can be used by a different user with their own authenticator app. ### Running as a Service #### systemd (Linux) Create `/etc/systemd/system/forward-auth.service`: ```ini [Unit] Description=Forward Auth TOTP Service After=network.target [Service] Type=simple User=www-data WorkingDirectory=/opt/forward-auth Environment="JWT_SECRET=your-secure-secret-here" ExecStart=/opt/forward-auth/forward-auth Restart=on-failure RestartSec=5s [Install] WantedBy=multi-user.target ``` Enable and start: ```bash sudo systemctl enable forward-auth sudo systemctl start forward-auth ``` ## Caddy Configuration ### Basic Configuration ```caddyfile # Your protected application app.example.com { forward_auth localhost:3000 { uri /verify copy_headers X-Original-URI } reverse_proxy localhost:8080 } # Auth service (optional, if you want it accessible externally) auth.example.com { reverse_proxy localhost:3000 } ``` ### Advanced Configuration with Error Handling ```caddyfile app.example.com { # Allow access to login page without auth @login { path /login* } handle @login { reverse_proxy localhost:3000 } # Protect everything else forward_auth localhost:3000 { uri /verify copy_headers X-Original-URI } reverse_proxy localhost:8080 } ``` ### Multiple Protected Services ```caddyfile # Auth service auth.example.com { reverse_proxy localhost:3000 } # Protected app 1 app1.example.com { forward_auth auth.example.com { uri /verify copy_headers X-Original-URI } reverse_proxy localhost:8080 } # Protected app 2 app2.example.com { forward_auth auth.example.com { uri /verify copy_headers X-Original-URI } reverse_proxy localhost:8081 } ``` ## API Endpoints | Endpoint | Method | Description | |----------|--------|-------------| | `/verify` | GET | Verify JWT token (for forward auth) | | `/login` | GET | Display login form | | `/login` | POST | Process OTP submission | | `/health` | GET | Health check endpoint | ## Security Considerations ### Production Checklist - [ ] Set a strong `JWT_SECRET` environment variable - [ ] Enable HTTPS and uncomment `Secure: true` in cookie settings - [ ] Restrict database file permissions: `chmod 600 auth.db` - [ ] Run the service as a non-root user - [ ] Keep the TOTP secrets secure and backed up - [ ] Consider rate limiting on the `/login` endpoint - [ ] Monitor failed authentication attempts ### HTTPS Configuration For production, uncomment the `Secure` flag in `main.go`: ```go http.SetCookie(w, &http.Cookie{ Name: jwtCookie, Value: tokenStr, Expires: time.Now().Add(sessionDuration), HttpOnly: true, SameSite: http.SameSiteLaxMode, Secure: true, // Uncomment this line Path: "/", }) ``` ## Database Management ### Backup ```bash cp auth.db auth.db.backup ``` ### View All Seeds ```bash sqlite3 auth.db "SELECT * FROM seeds;" ``` ### Remove a Seed ```bash sqlite3 auth.db "DELETE FROM seeds WHERE id = 1;" ``` ### Reset Database ```bash rm auth.db ./forward-auth # Will generate a new seed automatically ``` ## Troubleshooting ### "Invalid OTP" Error - Ensure your device time is synchronized (TOTP is time-based) - Check that you're using the correct seed in your authenticator app - Verify the OTP code hasn't expired (codes are valid for 30 seconds) ### Authentication Not Working - Check Caddy logs: `journalctl -u caddy -f` - Check forward-auth logs: `journalctl -u forward-auth -f` - Verify the forward auth service is running: `curl http://localhost:3000/health` - Ensure `X-Original-URI` header is being passed correctly ### Cookie Not Persisting - Verify you're using HTTPS in production with `Secure: true` - Check that the cookie path matches your application structure - Ensure `SameSite` attribute is compatible with your setup ## Performance This application is designed for light usage (1-20 users, <100 requests/day): - **Memory footprint:** ~10-15 MB - **CPU usage:** Minimal (<1% on modern systems) - **Database size:** <1 KB for up to 20 seeds - **Startup time:** <100ms ## Development ### Run in Development Mode ```bash go run main.go ``` ### Run Tests ```bash go test -v ./... ``` ### Build for Different Platforms ```bash # Linux GOOS=linux GOARCH=amd64 go build -o forward-auth-linux main.go # macOS GOOS=darwin GOARCH=amd64 go build -o forward-auth-macos main.go # Windows GOOS=windows GOARCH=amd64 go build -o forward-auth.exe main.go ``` ## Contributing Contributions are welcome! Please feel free to submit a Pull Request. ## License MIT License - see LICENSE file for details ## Acknowledgments - [golang-jwt/jwt](https://github.com/golang-jwt/jwt) - JWT implementation - [pquerna/otp](https://github.com/pquerna/otp) - TOTP implementation - [mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) - SQLite driver ## Support For issues and questions, please open an issue on GitHub. --- **Note:** This is a simple authentication service suitable for personal or small team use. For larger deployments, consider more robust authentication solutions with features like account lockout, audit logging, and multi-factor authentication options.