Some checks are pending
lint / docker (push) Waiting to run
All authentication methods (Basic Auth, Flask-Login, OAuth) are now installed as core dependencies instead of optional extras. This simplifies setup and eliminates the need to run `uv sync --extra auth-*` when switching between authentication methods. Changes: - Move authlib, bcrypt, and flask-login to core dependencies - Remove auth-* optional dependency groups from pyproject.toml - Update documentation to remove installation instructions - Simplify troubleshooting and migration guides Benefits: - No import errors when switching auth methods - Simpler user experience - All providers available out of the box 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
6.7 KiB
6.7 KiB
TISBackup Authentication Module
Pluggable authentication system for Flask routes.
Features
- Multiple providers: Basic Auth, Flask-Login, OAuth2
- Easy integration: Simple decorator-based protection
- Configurable: INI-based configuration
- Secure: bcrypt password hashing, OAuth integration
- Extensible: Easy to add new providers
Quick Start
1. Choose Authentication Provider
from libtisbackup.auth import get_auth_provider
# Get provider from config
auth = get_auth_provider("basic", {
"username": "admin",
"password": "$2b$12$...", # bcrypt hash
"use_bcrypt": True
})
# Initialize with Flask app
auth.init_app(app)
2. Protect Routes
@app.route("/")
@auth.require_auth
def index():
user = auth.get_current_user()
return f"Hello {user['username']}"
Providers
Base Provider (No Auth)
auth = get_auth_provider("none", {})
- No authentication required
- All routes publicly accessible
- Useful for development/testing
Basic Auth
auth = get_auth_provider("basic", {
"username": "admin",
"password": "$2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/LewY5GyYWv.5qVQK6",
"use_bcrypt": True,
"realm": "TISBackup"
})
Features:
- HTTP Basic Authentication
- bcrypt password hashing
- Custom realm support
Flask-Login
auth = get_auth_provider("flask-login", {
"users_file": "/etc/tis/users.txt",
"use_bcrypt": True,
"login_view": "login"
})
Features:
- Session-based authentication
- Multiple users support
- Login/logout pages
- bcrypt password hashing
User file format (users.txt
):
username:bcrypt_hash
admin:$2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/LewY5GyYWv.5qVQK6
OAuth2
auth = get_auth_provider("oauth", {
"provider": "google", # or "github", "gitlab", "generic"
"client_id": "your-client-id",
"client_secret": "your-client-secret",
"redirect_uri": "http://localhost:8080/oauth/callback",
"authorized_domains": ["example.com"],
"authorized_users": ["admin@example.com"]
})
Features:
- OAuth2 authentication
- Google, GitHub, GitLab support
- Custom OAuth providers
- Domain/user restrictions
API Reference
AuthProvider
Base class for all auth providers.
Methods
init_app(app)
- Initialize with Flask apprequire_auth(f)
- Decorator to protect routesis_authenticated()
- Check if current request is authenticatedget_current_user()
- Get current user infohandle_unauthorized()
- Handle unauthorized accesslogout()
- Logout current user
BasicAuthProvider
HTTP Basic Authentication provider.
Configuration
{
"username": str, # Required
"password": str, # Required (plain or bcrypt hash)
"use_bcrypt": bool, # Default: False
"realm": str # Default: "TISBackup"
}
FlaskLoginProvider
Session-based authentication provider.
Configuration
{
"users": dict, # {username: password_hash} or...
"users_file": str, # Path to users file
"use_bcrypt": bool, # Default: True
"login_view": str # Default: "login"
}
Methods
verify_password(username, password)
- Verify credentialslogin_user(username)
- Login user by username
OAuthProvider
OAuth2 authentication provider.
Configuration
{
"provider": str, # "google", "github", "gitlab", "generic"
"client_id": str, # Required
"client_secret": str, # Required
"redirect_uri": str, # Required
"scopes": list, # Optional
"authorized_domains": list, # Optional
"authorized_users": list, # Optional
# For generic provider:
"authorization_endpoint": str,
"token_endpoint": str,
"userinfo_endpoint": str
}
Methods
is_user_authorized(user_info)
- Check if user is authorized
Integration Example
See example_integration.py for a complete example.
Minimal Example
from flask import Flask
from libtisbackup.auth import get_auth_provider
app = Flask(__name__)
app.secret_key = "your-secret-key"
# Initialize auth
auth = get_auth_provider("basic", {
"username": "admin",
"password": "changeme",
"use_bcrypt": False
})
auth.init_app(app)
# Protected route
@app.route("/")
@auth.require_auth
def index():
return "Protected content"
# Public route
@app.route("/health")
def health():
return "OK"
With Flask-Login
auth = get_auth_provider("flask-login", {
"users": {
"admin": "$2b$12$..."
},
"use_bcrypt": True
})
auth.init_app(app)
@app.route("/login", methods=["POST"])
def login():
username = request.form["username"]
password = request.form["password"]
if auth.verify_password(username, password):
auth.login_user(username)
return redirect("/")
return "Invalid credentials", 401
@app.route("/")
@auth.require_auth
def index():
user = auth.get_current_user()
return f"Hello {user['username']}"
With OAuth
auth = get_auth_provider("oauth", {
"provider": "google",
"client_id": "...",
"client_secret": "...",
"redirect_uri": "http://localhost:8080/oauth/callback",
"authorized_domains": ["example.com"]
})
auth.init_app(app)
@app.route("/oauth/login")
def oauth_login():
return auth.oauth_client.authorize_redirect(
url_for("oauth_callback", _external=True)
)
@app.route("/oauth/callback")
def oauth_callback():
token = auth.oauth_client.authorize_access_token()
user_info = auth.oauth_client.get(auth.userinfo_endpoint).json()
if auth.is_user_authorized(user_info):
session["oauth_user"] = user_info
return redirect("/")
return "Unauthorized", 403
Security Considerations
- Always use HTTPS in production - Especially for Basic Auth
- Use bcrypt for passwords - Never store plain text passwords
- Rotate credentials regularly - Change passwords and OAuth secrets
- Restrict OAuth access - Use
authorized_domains
orauthorized_users
- Set strong Flask secret_key - Use
secrets.token_hex(32)
- Protect config files -
chmod 600
for files with credentials - Use environment variables - For sensitive configuration values
Testing
import unittest
from libtisbackup.auth import get_auth_provider
class TestBasicAuth(unittest.TestCase):
def test_authentication(self):
auth = get_auth_provider("basic", {
"username": "admin",
"password": "test",
"use_bcrypt": False
})
# Mock request with credentials
# Test authentication logic
pass
License
GPL v3.0 - Same as TISBackup