160 lines
7.0 KiB
Python
160 lines
7.0 KiB
Python
"""Base configuration for all environments."""
|
|
import os
|
|
from datetime import timedelta
|
|
|
|
|
|
class BaseConfig:
|
|
"""Base configuration class with common settings."""
|
|
|
|
# Application
|
|
SECRET_KEY = os.getenv("SECRET_KEY", "dev-secret-key-change-in-production")
|
|
DEBUG = False
|
|
TESTING = False
|
|
|
|
# Database
|
|
SQLALCHEMY_DATABASE_URI = os.getenv(
|
|
"DATABASE_URL", "postgresql://postgres:postgres@localhost:5432/authy2"
|
|
)
|
|
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
|
SQLALCHEMY_ECHO = os.getenv("SQLALCHEMY_ECHO", "False").lower() == "true"
|
|
SQLALCHEMY_LOG_LEVEL = os.getenv("SQLALCHEMY_LOG_LEVEL", "WARNING")
|
|
SQLALCHEMY_ENGINE_OPTIONS = {
|
|
"pool_pre_ping": True,
|
|
"pool_recycle": 300,
|
|
}
|
|
|
|
# Security
|
|
BCRYPT_LOG_ROUNDS = int(os.getenv("BCRYPT_LOG_ROUNDS", "12"))
|
|
|
|
# Encryption key for sensitive data (client secrets, tokens, etc.)
|
|
ENCRYPTION_KEY = os.getenv("ENCRYPTION_KEY", "dev-encryption-key-change-in-production")
|
|
|
|
# Encryption key for CA private keys stored in the database.
|
|
# Must be set to a strong random secret in production.
|
|
# Any string is accepted — it is SHA-256 derived to a 32-byte Fernet key internally.
|
|
CA_ENCRYPTION_KEY = os.getenv("CA_ENCRYPTION_KEY", "dev-ca-encryption-key-change-in-production")
|
|
|
|
# Session configuration for WebAuthn cross-origin support
|
|
SESSION_COOKIE_SECURE = os.getenv("SESSION_COOKIE_SECURE", "True").lower() == "true"
|
|
SESSION_COOKIE_HTTPONLY = True
|
|
SESSION_COOKIE_SAMESITE = os.getenv("SESSION_COOKIE_SAMESITE", "None")
|
|
|
|
# Set the cookie domain to allow sharing across subdomains (e.g., ui.webauthn.local and api.webauthn.local)
|
|
# Extract base domain from WEBAUTHN_RP_ID or use default
|
|
_rp_id = os.getenv("WEBAUTHN_RP_ID", "localhost")
|
|
SESSION_COOKIE_DOMAIN = os.getenv("SESSION_COOKIE_DOMAIN", _rp_id if _rp_id != "localhost" else None)
|
|
|
|
PERMANENT_SESSION_LIFETIME = timedelta(
|
|
seconds=int(os.getenv("MAX_SESSION_DURATION", "86400"))
|
|
)
|
|
|
|
# Session timeout policy (seconds)
|
|
SESSION_IDLE_TIMEOUT = int(os.getenv("SESSION_IDLE_TIMEOUT", "900"))
|
|
SESSION_ABSOLUTE_TIMEOUT = int(os.getenv("SESSION_ABSOLUTE_TIMEOUT", "28800"))
|
|
|
|
# CORS
|
|
CORS_ORIGINS = os.getenv(
|
|
"CORS_ORIGINS",
|
|
"https://ui.webauthn.local,https://ui.webauthn.local:5173,http://localhost:3000,http://localhost:5173"
|
|
).split(",")
|
|
CORS_SUPPORTS_CREDENTIALS = True
|
|
|
|
# JWT (if using JWT)
|
|
JWT_SECRET_KEY = os.getenv("JWT_SECRET_KEY", SECRET_KEY)
|
|
JWT_ACCESS_TOKEN_EXPIRES = timedelta(
|
|
seconds=int(os.getenv("JWT_ACCESS_TOKEN_EXPIRES", "3600"))
|
|
)
|
|
JWT_REFRESH_TOKEN_EXPIRES = timedelta(
|
|
seconds=int(os.getenv("JWT_REFRESH_TOKEN_EXPIRES", "2592000"))
|
|
)
|
|
|
|
# Redis
|
|
REDIS_URL = os.getenv("REDIS_URL", "redis://localhost:6379/0")
|
|
|
|
# Flask Session configuration - use Redis for better cross-instance support
|
|
SESSION_TYPE = os.getenv("SESSION_TYPE", "redis")
|
|
SESSION_REDIS_URL = os.getenv("SESSION_REDIS_URL", REDIS_URL)
|
|
SESSION_REDIS = None # Will be set at app initialization
|
|
|
|
# Rate Limiting
|
|
RATELIMIT_ENABLED = os.getenv("RATELIMIT_ENABLED", "False").lower() == "true"
|
|
RATELIMIT_STORAGE_URL = os.getenv("RATELIMIT_STORAGE_URL", "redis://localhost:6379/1")
|
|
RATELIMIT_DEFAULT = "100/hour"
|
|
|
|
# Per-endpoint auth rate limits (override via env vars for each environment)
|
|
RATELIMIT_AUTH_REGISTER = os.getenv("RATELIMIT_AUTH_REGISTER", "10 per minute; 50 per hour")
|
|
RATELIMIT_AUTH_LOGIN = os.getenv("RATELIMIT_AUTH_LOGIN", "20 per minute; 100 per hour")
|
|
RATELIMIT_AUTH_TOTP_VERIFY = os.getenv("RATELIMIT_AUTH_TOTP_VERIFY", "20 per minute; 100 per hour")
|
|
RATELIMIT_AUTH_FORGOT_PASSWORD = os.getenv("RATELIMIT_AUTH_FORGOT_PASSWORD", "5 per minute; 20 per hour")
|
|
RATELIMIT_AUTH_RESET_PASSWORD = os.getenv("RATELIMIT_AUTH_RESET_PASSWORD", "10 per minute; 30 per hour")
|
|
RATELIMIT_AUTH_RESEND_VERIFICATION = os.getenv("RATELIMIT_AUTH_RESEND_VERIFICATION", "5 per minute; 20 per hour")
|
|
|
|
# Logging
|
|
LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO")
|
|
LOG_TO_STDOUT = os.getenv("LOG_TO_STDOUT", "False").lower() == "true"
|
|
|
|
# OIDC Configuration
|
|
OIDC_ISSUER_URL = os.getenv("OIDC_ISSUER_URL", "http://localhost:5000")
|
|
OIDC_BASE_URL = os.getenv("OIDC_BASE_URL", OIDC_ISSUER_URL)
|
|
|
|
# Token lifetimes
|
|
OIDC_ACCESS_TOKEN_LIFETIME = int(os.getenv("OIDC_ACCESS_TOKEN_LIFETIME", "3600"))
|
|
OIDC_REFRESH_TOKEN_LIFETIME = int(os.getenv("OIDC_REFRESH_TOKEN_LIFETIME", "2592000"))
|
|
OIDC_ID_TOKEN_LIFETIME = int(os.getenv("OIDC_ID_TOKEN_LIFETIME", "3600"))
|
|
OIDC_AUTHORIZATION_CODE_LIFETIME = int(os.getenv("OIDC_AUTHORIZATION_CODE_LIFETIME", "600"))
|
|
|
|
# Security settings
|
|
OIDC_REQUIRE_PKCE = os.getenv("OIDC_REQUIRE_PKCE", "True").lower() == "true"
|
|
OIDC_ALLOW_IMPLICIT_FLOW = os.getenv("OIDC_ALLOW_IMPLICIT_FLOW", "False").lower() == "true"
|
|
OIDC_SUPPORTED_SCOPES = ["openid", "profile", "email"]
|
|
OIDC_DEFAULT_SCOPES = ["openid", "profile", "email"]
|
|
|
|
# Key rotation
|
|
OIDC_KEY_ROTATION_DAYS = int(os.getenv("OIDC_KEY_ROTATION_DAYS", "90"))
|
|
OIDC_KEY_GRACE_PERIOD_DAYS = int(os.getenv("OIDC_KEY_GRACE_PERIOD_DAYS", "30"))
|
|
|
|
# Rate limiting
|
|
OIDC_RATE_LIMIT_AUTHORIZE = os.getenv("OIDC_RATE_LIMIT_AUTHORIZE", "10/minute")
|
|
OIDC_RATE_LIMIT_TOKEN = os.getenv("OIDC_RATE_LIMIT_TOKEN", "20/minute")
|
|
OIDC_RATE_LIMIT_USERINFO = os.getenv("OIDC_RATE_LIMIT_USERINFO", "60/minute")
|
|
|
|
# API Versioning
|
|
API_VERSION = "1.0.0"
|
|
ENVELOPE_VERSION = "1.0"
|
|
|
|
# Pagination
|
|
DEFAULT_PAGE_SIZE = 20
|
|
MAX_PAGE_SIZE = 100
|
|
|
|
# WebAuthn Configuration
|
|
WEBAUTHN_RP_ID = os.getenv("WEBAUTHN_RP_ID", "localhost")
|
|
WEBAUTHN_RP_NAME = os.getenv("WEBAUTHN_RP_NAME", "Secuird")
|
|
WEBAUTHN_ORIGIN = os.getenv("WEBAUTHN_ORIGIN", "https://ui.webauthn.local")
|
|
|
|
# Frontend URL (for OAuth callback redirects)
|
|
FRONTEND_URL = os.getenv("FRONTEND_URL", "http://localhost:8080")
|
|
APP_URL = os.getenv("APP_URL", os.getenv("FRONTEND_URL", "http://localhost:8080"))
|
|
OIDC_UI_URL = os.getenv("OIDC_UI_URL", os.getenv("FRONTEND_URL", "http://localhost:8080"))
|
|
|
|
# ZeroTier Configuration
|
|
|
|
# Email / SMTP
|
|
EMAIL_ENABLED = os.getenv("EMAIL_ENABLED", "False").lower() == "true"
|
|
SMTP_HOST = os.getenv("SMTP_HOST", "smtp.gmail.com")
|
|
SMTP_PORT = int(os.getenv("SMTP_PORT", "587"))
|
|
SMTP_USERNAME = os.getenv("SMTP_USERNAME", "")
|
|
SMTP_PASSWORD = os.getenv("SMTP_PASSWORD", "")
|
|
FROM_ADDRESS = os.getenv("FROM_ADDRESS", "noreply@gatehouse.local")
|
|
|
|
# Email Provider Configuration
|
|
EMAIL_PROVIDER = os.getenv("EMAIL_PROVIDER", "smtp").lower() # smtp, mailgun, sendgrid
|
|
|
|
# Mailgun Configuration (used when EMAIL_PROVIDER=mailgun)
|
|
MAILGUN_API_KEY = os.getenv("MAILGUN_API_KEY", "")
|
|
MAILGUN_DOMAIN = os.getenv("MAILGUN_DOMAIN", "")
|
|
MAILGUN_API_URL = os.getenv("MAILGUN_API_URL", "https://api.mailgun.net/v3")
|
|
|
|
# SendGrid Configuration (used when EMAIL_PROVIDER=sendgrid)
|
|
SENDGRID_API_KEY = os.getenv("SENDGRID_API_KEY", "")
|
|
SENDGRID_FROM_EMAIL = os.getenv("SENDGRID_FROM_EMAIL", "")
|