TISbackup/AUTHENTICATION.md
k3nny f12d89f3da feat(auth): add pluggable authentication system for Flask routes
Implement comprehensive authentication system with support for
Basic Auth, Flask-Login, and OAuth2 providers.

Features:
- Pluggable architecture via factory pattern
- Multiple authentication providers:
  * None: No authentication (development/testing)
  * Basic Auth: HTTP Basic with bcrypt support
  * Flask-Login: Session-based with multiple users
  * OAuth2: Google, GitHub, GitLab, and generic providers
- Decorator-based route protection (@auth.require_auth)
- User authorization by domain or email (OAuth)
- bcrypt password hashing support
- Comprehensive documentation and examples

Components:
- libtisbackup/auth/__init__.py: Factory function and exports
- libtisbackup/auth/base.py: Base provider interface
- libtisbackup/auth/basic_auth.py: HTTP Basic Auth implementation
- libtisbackup/auth/flask_login_auth.py: Flask-Login implementation
- libtisbackup/auth/oauth_auth.py: OAuth2 implementation
- libtisbackup/auth/example_integration.py: Integration examples
- libtisbackup/auth/README.md: API reference and examples

Documentation:
- AUTHENTICATION.md: Complete authentication guide
  * Setup instructions for each provider
  * Configuration examples
  * Security best practices
  * Troubleshooting guide
  * Migration guide
- samples/auth-config-examples.ini: Configuration templates

Dependencies:
- Add optional dependencies in pyproject.toml:
  * auth-basic: bcrypt>=4.0.0
  * auth-login: flask-login>=0.6.0, bcrypt>=4.0.0
  * auth-oauth: authlib>=1.3.0, requests>=2.32.0
  * auth-all: All auth providers

Installation:
```bash
# Install specific provider
uv sync --extra auth-basic

# Install all providers
uv sync --extra auth-all
```

Usage:
```python
from libtisbackup.auth import get_auth_provider

# Initialize
auth = get_auth_provider("basic", {
    "username": "admin",
    "password": "$2b$12$...",
    "use_bcrypt": True
})
auth.init_app(app)

# Protect routes
@app.route("/")
@auth.require_auth
def index():
    user = auth.get_current_user()
    return f"Hello {user['username']}"
```

Security features:
- bcrypt password hashing (work factor 12)
- OAuth domain/user restrictions
- Session-based authentication
- Clear separation of concerns
- Environment variable support for secrets

OAuth providers supported:
- Google (OpenID Connect)
- GitHub
- GitLab
- Generic OAuth2 provider

Breaking change: None - new feature, backward compatible
Users can continue without authentication (type=none)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-05 02:02:46 +02:00

444 lines
9.6 KiB
Markdown

# TISBackup Authentication System
TISBackup provides a pluggable authentication system for securing the Flask web interface. You can choose between multiple authentication methods based on your security requirements.
## Supported Authentication Methods
1. **None** - No authentication (default, NOT recommended for production)
2. **Basic Auth** - HTTP Basic Authentication with username/password
3. **Flask-Login** - Session-based authentication with username/password
4. **OAuth2** - OAuth authentication (Google, GitHub, GitLab, or generic provider)
## Quick Start
### 1. Choose Authentication Method
Add an `[authentication]` section to `/etc/tis/tisbackup_gui.ini`:
```ini
[authentication]
type = basic
username = admin
password = $2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/LewY5GyYWv.5qVQK6
use_bcrypt = True
```
### 2. Install Dependencies
```bash
# For Basic Auth
uv sync --extra auth-basic
# For Flask-Login
uv sync --extra auth-login
# For OAuth
uv sync --extra auth-oauth
# For all auth methods
uv sync --extra auth-all
```
### 3. Restart TISBackup
```bash
docker compose restart tisbackup_gui
```
## Configuration Guide
### Basic Authentication
Simple HTTP Basic Auth with username and password.
**Pros:**
- Easy to set up
- Works with all HTTP clients
- No session management needed
**Cons:**
- Credentials sent with every request
- No logout functionality
- Browser password prompt can be confusing
**Configuration:**
```ini
[authentication]
type = basic
username = admin
# Use bcrypt hash (recommended)
password = $2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/LewY5GyYWv.5qVQK6
use_bcrypt = True
realm = TISBackup Admin
```
**Generate bcrypt hash:**
```bash
python3 -c "import bcrypt; print(bcrypt.hashpw(b'yourpassword', bcrypt.gensalt()).decode())"
```
**Docker environment:**
```yaml
services:
tisbackup_gui:
environment:
- TISBACKUP_SECRET_KEY=your-secret-key
# Optional: Pass credentials via env vars
# Then reference in config with ${AUTH_PASSWORD}
```
---
### Flask-Login Authentication
Session-based authentication with login page and user management.
**Pros:**
- Clean login/logout workflow
- Session-based (no credentials in each request)
- Multiple users supported
- Password hashing with bcrypt
**Cons:**
- Requires custom login page
- Session management overhead
- Cookies must be enabled
**Configuration:**
```ini
[authentication]
type = flask-login
users_file = /etc/tis/users.txt
use_bcrypt = True
login_view = login
```
**Create users file** (`/etc/tis/users.txt`):
```
admin:$2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/LewY5GyYWv.5qVQK6
operator:$2b$12$abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNO
viewer:$2b$12$ANOTHERBCRYPTHASHHERE1234567890ABCDEFGHIJKLMNOPQRS
```
**Generate user entry:**
```bash
USERNAME="admin"
PASSWORD="yourpassword"
HASH=$(python3 -c "import bcrypt; print(bcrypt.hashpw(b'$PASSWORD', bcrypt.gensalt()).decode())")
echo "$USERNAME:$HASH" >> /etc/tis/users.txt
```
**Permissions:**
```bash
chmod 600 /etc/tis/users.txt
chown root:root /etc/tis/users.txt
```
---
### OAuth2 Authentication
Delegate authentication to external OAuth providers (Google, GitHub, GitLab, etc.)
**Pros:**
- No password management
- Leverage existing identity providers
- Support for SSO
- Can restrict by domain or specific users
**Cons:**
- Requires OAuth app registration
- Internet connectivity required
- More complex setup
- External dependency
#### Google OAuth
**Setup:**
1. Go to [Google Cloud Console](https://console.cloud.google.com/apis/credentials)
2. Create OAuth 2.0 Client ID
3. Add authorized redirect URI: `http://your-server:8080/oauth/callback`
4. Note the Client ID and Client Secret
**Configuration:**
```ini
[authentication]
type = oauth
provider = google
client_id = 123456789-abcdefghijklmnop.apps.googleusercontent.com
client_secret = GOCSPX-your-client-secret-here
redirect_uri = http://your-server:8080/oauth/callback
# Restrict to specific domain(s)
authorized_domains = example.com,mycompany.com
# Or restrict to specific users
authorized_users = admin@example.com,backup-admin@example.com
```
#### GitHub OAuth
**Setup:**
1. Go to GitHub Settings > Developer settings > [OAuth Apps](https://github.com/settings/developers)
2. Register a new application
3. Set Authorization callback URL: `http://your-server:8080/oauth/callback`
4. Note the Client ID and Client Secret
**Configuration:**
```ini
[authentication]
type = oauth
provider = github
client_id = your-github-client-id
client_secret = your-github-client-secret
redirect_uri = http://your-server:8080/oauth/callback
authorized_users = admin@example.com,devops@example.com
```
#### GitLab OAuth
**Setup:**
1. Go to GitLab User Settings > [Applications](https://gitlab.com/-/profile/applications)
2. Create application with scopes: `read_user`, `email`
3. Set Redirect URI: `http://your-server:8080/oauth/callback`
4. Note the Application ID and Secret
**Configuration:**
```ini
[authentication]
type = oauth
provider = gitlab
client_id = your-gitlab-application-id
client_secret = your-gitlab-secret
redirect_uri = http://your-server:8080/oauth/callback
authorized_domains = example.com
```
#### Generic OAuth Provider
For custom OAuth providers:
```ini
[authentication]
type = oauth
provider = generic
client_id = your-client-id
client_secret = your-client-secret
redirect_uri = http://your-server:8080/oauth/callback
# Custom endpoints
authorization_endpoint = https://auth.example.com/oauth/authorize
token_endpoint = https://auth.example.com/oauth/token
userinfo_endpoint = https://auth.example.com/oauth/userinfo
scopes = openid,email,profile
# Authorization rules
authorized_domains = example.com
```
---
## Security Best Practices
### 1. Use HTTPS in Production
Always use a reverse proxy with TLS:
```nginx
server {
listen 443 ssl http2;
server_name tisbackup.example.com;
ssl_certificate /etc/letsencrypt/live/tisbackup.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/tisbackup.example.com/privkey.pem;
location / {
proxy_pass http://localhost:8080/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
}
```
### 2. Set Strong Flask Secret Key
```bash
# Generate secret
python3 -c "import secrets; print(secrets.token_hex(32))"
# Set in environment
export TISBACKUP_SECRET_KEY=your-generated-secret-key
```
### 3. Protect Configuration Files
```bash
chmod 600 /etc/tis/tisbackup_gui.ini
chmod 600 /etc/tis/users.txt # if using Flask-Login
chown root:root /etc/tis/*.ini
```
### 4. Use Environment Variables for Secrets
Instead of hardcoding secrets in config files:
```ini
[authentication]
type = oauth
client_id = ${OAUTH_CLIENT_ID}
client_secret = ${OAUTH_CLIENT_SECRET}
```
Then set in Docker Compose:
```yaml
services:
tisbackup_gui:
environment:
- OAUTH_CLIENT_ID=your-client-id
- OAUTH_CLIENT_SECRET=your-client-secret
- TISBACKUP_SECRET_KEY=your-secret-key
```
### 5. Regularly Rotate Credentials
- Change passwords/secrets every 90 days
- Rotate OAuth client secrets annually
- Review authorized users/domains regularly
### 6. Monitor Authentication Logs
Check logs for failed authentication attempts:
```bash
docker logs tisbackup_gui | grep -i "auth"
```
## Troubleshooting
### Basic Auth Not Working
1. **Check bcrypt installation:**
```bash
uv sync --extra auth-basic
```
2. **Verify password hash:**
```bash
python3 -c "import bcrypt; print(bcrypt.checkpw(b'yourpassword', b'$2b$12$your-hash'))"
```
3. **Check browser credentials:**
- Clear browser cache
- Try incognito/private mode
### Flask-Login Issues
1. **Users file not found:**
```bash
ls -la /etc/tis/users.txt
chmod 600 /etc/tis/users.txt
```
2. **Module not found:**
```bash
uv sync --extra auth-login
```
3. **Session problems:**
- Check `TISBACKUP_SECRET_KEY` is set
- Ensure cookies are enabled
### OAuth Problems
1. **Redirect URI mismatch:**
- Ensure redirect URI in config matches OAuth app settings exactly
- Check for http vs https mismatch
2. **Unauthorized domain/user:**
- Verify email matches `authorized_users` or domain matches `authorized_domains`
- Check OAuth provider returns email in user info
3. **Module not found:**
```bash
uv sync --extra auth-oauth
```
4. **Token errors:**
- Verify client ID and secret are correct
- Check OAuth app is enabled
- Ensure scopes are correct
## API Access with Authentication
### Basic Auth
```bash
curl -u admin:password http://localhost:8080/api/backups
```
### OAuth (with access token)
```bash
# Not recommended for API access - use service account or API keys instead
```
### Recommendation for API Access
For programmatic API access, use Basic Auth with a dedicated API user:
```ini
[authentication]
type = basic
username = api-user
password = $2b$12$...
```
Or implement API key authentication separately for API endpoints.
## Migration Guide
### From No Auth to Basic Auth
1. Add authentication section to config
2. Install bcrypt: `uv sync --extra auth-basic`
3. Restart service
4. Update client scripts with credentials
### From Basic Auth to OAuth
1. Register OAuth application
2. Update configuration
3. Install dependencies: `uv sync --extra auth-oauth`
4. Test OAuth login flow
5. Update redirect URI if needed
### From Flask-Login to OAuth
1. Register OAuth application
2. Map user emails to OAuth provider
3. Update configuration
4. Test migration with test users first
## Support
For issues or questions:
- Check logs: `docker logs tisbackup_gui`
- Review configuration syntax
- Verify dependencies are installed
- See [SECURITY_IMPROVEMENTS.md](../SECURITY_IMPROVEMENTS.md) for security context