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

9.6 KiB

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:

[authentication]
type = basic
username = admin
password = $2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/LewY5GyYWv.5qVQK6
use_bcrypt = True

2. Install Dependencies

# 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

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:

[authentication]
type = basic
username = admin
# Use bcrypt hash (recommended)
password = $2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/LewY5GyYWv.5qVQK6
use_bcrypt = True
realm = TISBackup Admin

Generate bcrypt hash:

python3 -c "import bcrypt; print(bcrypt.hashpw(b'yourpassword', bcrypt.gensalt()).decode())"

Docker environment:

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:

[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:

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:

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
  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:

[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
  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:

[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
  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:

[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:

[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:

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

# 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

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:

[authentication]
type = oauth
client_id = ${OAUTH_CLIENT_ID}
client_secret = ${OAUTH_CLIENT_SECRET}

Then set in Docker Compose:

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:

docker logs tisbackup_gui | grep -i "auth"

Troubleshooting

Basic Auth Not Working

  1. Check bcrypt installation:

    uv sync --extra auth-basic
    
  2. Verify password hash:

    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:

    ls -la /etc/tis/users.txt
    chmod 600 /etc/tis/users.txt
    
  2. Module not found:

    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:

    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

curl -u admin:password http://localhost:8080/api/backups

OAuth (with access token)

# 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:

[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 for security context