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_authdirective - 📱 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
git clone https://github.com/yourusername/forward-auth-totp.git
cd forward-auth-totp
Install dependencies
go mod download
Build the application
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:
export JWT_SECRET="your-very-secure-random-secret-here-min-32-chars"
Generate a secure secret:
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:
./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:
./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:
[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:
sudo systemctl enable forward-auth
sudo systemctl start forward-auth
Caddy Configuration
Basic Configuration
# 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
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
# 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_SECRETenvironment variable - Enable HTTPS and uncomment
Secure: truein 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
/loginendpoint - Monitor failed authentication attempts
HTTPS Configuration
For production, uncomment the Secure flag in main.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
cp auth.db auth.db.backup
View All Seeds
sqlite3 auth.db "SELECT * FROM seeds;"
Remove a Seed
sqlite3 auth.db "DELETE FROM seeds WHERE id = 1;"
Reset Database
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-URIheader 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
SameSiteattribute 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
go run main.go
Run Tests
go test -v ./...
Build for Different Platforms
# 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 - JWT implementation
- pquerna/otp - TOTP implementation
- 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.