Chore: Setup and Env

This commit is contained in:
2026-03-06 01:36:23 +05:45
parent 1789590167
commit 16d04bd5f7
5 changed files with 162 additions and 45 deletions
+91 -32
View File
@@ -1,58 +1,117 @@
# Flask Configuration FLASK_APP=manage.py
FLASK_APP=wsgi.py
FLASK_ENV=development FLASK_ENV=development
SECRET_KEY=your-secret-key-here-change-in-production FLASK_DEBUG=1
# Database # Database
DATABASE_URL=postgresql://user:password@localhost:5432/authy2_dev DATABASE_URL=postgresql://user:password@localhost:5432/gatehouse_dev
SQLALCHEMY_ECHO=False SQLALCHEMY_ECHO=False
SQLALCHEMY_LOG_LEVEL=WARNING SQLALCHEMY_LOG_LEVEL=WARNING
# Security # Security / Encryption
SECRET_KEY=change-me-in-production
ENCRYPTION_KEY=change-me-in-production-32-bytes!!
# Used to encrypt SSH CA private keys stored in the database
CA_ENCRYPTION_KEY=change-me-in-production
BCRYPT_LOG_ROUNDS=12 BCRYPT_LOG_ROUNDS=12
ENCRYPTION_KEY=your-encryption-key-here-change-in-production
# Session cookies
SESSION_COOKIE_SECURE=False SESSION_COOKIE_SECURE=False
SESSION_COOKIE_HTTPONLY=True
SESSION_COOKIE_SAMESITE=Lax SESSION_COOKIE_SAMESITE=Lax
# Only needed when sharing cookies across subdomains (e.g. api.example.com + ui.example.com)
# SESSION_COOKIE_DOMAIN=example.com
MAX_SESSION_DURATION=86400 MAX_SESSION_DURATION=86400
# CORS # ─────────────────────────────────────────────────────────────────────────────
#CORS_ORIGINS=http://localhost:3000,http://localhost:5173,https://oidc-playpen.lovable.app/,http://localhost:8080/ # JWT
CORS_ORIGINS=* # ─────────────────────────────────────────────────────────────────────────────
JWT_SECRET_KEY=change-me-in-production
# JWT (if using JWT instead of sessions)
JWT_SECRET_KEY=your-jwt-secret-key-here
JWT_ACCESS_TOKEN_EXPIRES=3600 JWT_ACCESS_TOKEN_EXPIRES=3600
JWT_REFRESH_TOKEN_EXPIRES=2592000 JWT_REFRESH_TOKEN_EXPIRES=2592000
# Redis (for session storage) # ─────────────────────────────────────────────────────────────────────────────
# Redis (session storage + rate limiting)
# ─────────────────────────────────────────────────────────────────────────────
REDIS_URL=redis://localhost:6379/0 REDIS_URL=redis://localhost:6379/0
SESSION_REDIS_URL=redis://localhost:6379/0
RATELIMIT_STORAGE_URL=redis://localhost:6379/1
# OIDC # ─────────────────────────────────────────────────────────────────────────────
# CORS
# ─────────────────────────────────────────────────────────────────────────────
CORS_ORIGINS=http://localhost:8080,http://localhost:5173
# ─────────────────────────────────────────────────────────────────────────────
# Frontend / App URLs
# All three should point at the browser-facing SPA. They are used for:
# FRONTEND_URL → OAuth callback redirects after provider auth
# APP_URL → Password-reset and email-verify links in emails
# OIDC_UI_URL → OIDC /authorize redirects to the React consent/login UI
# ─────────────────────────────────────────────────────────────────────────────
FRONTEND_URL=http://localhost:8080
APP_URL=http://localhost:8080
OIDC_UI_URL=http://localhost:8080
# ─────────────────────────────────────────────────────────────────────────────
# OIDC / OAuth issuer
# ─────────────────────────────────────────────────────────────────────────────
OIDC_ISSUER_URL=http://localhost:5000 OIDC_ISSUER_URL=http://localhost:5000
OIDC_BASE_URL=http://localhost:5000
# ─────────────────────────────────────────────────────────────────────────────
# WebAuthn
# ─────────────────────────────────────────────────────────────────────────────
WEBAUTHN_RP_ID=localhost
WEBAUTHN_RP_NAME=Gatehouse
WEBAUTHN_ORIGIN=http://localhost:8080
# ─────────────────────────────────────────────────────────────────────────────
# SSH CA (pick one)
# ─────────────────────────────────────────────────────────────────────────────
SSH_CA_KEY_PATH=/path/to/ca-users
# SSH_CA_PRIVATE_KEY= # raw key content; takes priority over SSH_CA_KEY_PATH
# ─────────────────────────────────────────────────────────────────────────────
# Email / SMTP
# ─────────────────────────────────────────────────────────────────────────────
EMAIL_ENABLED=False
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USE_TLS=True
SMTP_USERNAME=
SMTP_PASSWORD=
FROM_ADDRESS=noreply@gatehouse.local
# ─────────────────────────────────────────────────────────────────────────────
# Logging # Logging
# ─────────────────────────────────────────────────────────────────────────────
LOG_LEVEL=INFO LOG_LEVEL=INFO
LOG_TO_STDOUT=True LOG_TO_STDOUT=True
# ─────────────────────────────────────────────────────────────────────────────
# Rate Limiting # Rate Limiting
# ─────────────────────────────────────────────────────────────────────────────
RATELIMIT_ENABLED=True RATELIMIT_ENABLED=True
RATELIMIT_STORAGE_URL=redis://localhost:6379/1 # Per-endpoint auth limits (optional — defaults shown)
# RATELIMIT_AUTH_REGISTER=10 per minute; 50 per hour
# SSH CA # RATELIMIT_AUTH_LOGIN=20 per minute; 100 per hour
# Path to CA private key file (alternative to SSH_CA_PRIVATE_KEY env var) # RATELIMIT_AUTH_TOTP_VERIFY=20 per minute; 100 per hour
SSH_CA_KEY_PATH=/path/to/ca-users # RATELIMIT_AUTH_FORGOT_PASSWORD=5 per minute; 20 per hour
# Or set the key content directly (takes priority over SSH_CA_KEY_PATH): # RATELIMIT_AUTH_RESET_PASSWORD=10 per minute; 30 per hour
# SSH_CA_PRIVATE_KEY=
EMAIL_ENABLED=
SMTP_HOST=
SMTP_PORT=
SMTP_USERNAME=
SMTP_PASSWORD=
FROM_ADDRESS=
WEBAUTHN_ORIGIN=
ZEROTIER_API_TOKEN= ZEROTIER_API_TOKEN=
ZEROTIER_API_URL= ZEROTIER_API_URL=
# ─────────────────────────────────────────────────────────────────────────────
# OIDC token lifetimes & security (optional — defaults shown)
# ─────────────────────────────────────────────────────────────────────────────
# OIDC_ACCESS_TOKEN_LIFETIME=3600
# OIDC_REFRESH_TOKEN_LIFETIME=2592000
# OIDC_ID_TOKEN_LIFETIME=3600
# OIDC_AUTHORIZATION_CODE_LIFETIME=600
# OIDC_REQUIRE_PKCE=True
# OIDC_ALLOW_IMPLICIT_FLOW=False
# OIDC_KEY_ROTATION_DAYS=90
# OIDC_KEY_GRACE_PERIOD_DAYS=30
# OIDC_RATE_LIMIT_AUTHORIZE=10/minute
# OIDC_RATE_LIMIT_TOKEN=20/minute
# OIDC_RATE_LIMIT_USERINFO=60/minute
+2
View File
@@ -128,6 +128,8 @@ class BaseConfig:
# Frontend URL (for OAuth callback redirects) # Frontend URL (for OAuth callback redirects)
FRONTEND_URL = os.getenv("FRONTEND_URL", "http://localhost:8080") 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 # ZeroTier Configuration
ZEROTIER_API_TOKEN = os.getenv("ZEROTIER_API_TOKEN", "") ZEROTIER_API_TOKEN = os.getenv("ZEROTIER_API_TOKEN", "")
+61 -12
View File
@@ -1,5 +1,6 @@
"""Management script for Flask application.""" """Management script for Flask application."""
import os import os
import click
from dotenv import load_dotenv from dotenv import load_dotenv
# Load environment variables FIRST, before any app imports # Load environment variables FIRST, before any app imports
@@ -153,36 +154,75 @@ def mfa_compliance_status():
@cli.command("configure_oauth") @cli.command("configure_oauth")
def configure_oauth(): @click.argument("provider", required=False)
"""Interactively configure an OAuth provider at the application level. @click.option("--client-id", default=None, help="OAuth client ID")
@click.option("--client-secret", default=None, help="OAuth client secret")
@click.option("--redirect-url", default=None, help="Default redirect URL (e.g. https://yourdomain.com/api/v1/auth/external/<provider>/callback)")
def configure_oauth(provider, client_id, client_secret, redirect_url):
"""Configure an OAuth provider at the application level.
Usage: Usage (interactive):
python manage.py configure_oauth python manage.py configure_oauth
Usage (non-interactive):
python manage.py configure_oauth google --client-id ID --client-secret SECRET
Supported providers: google, github, microsoft Supported providers: google, github, microsoft
""" """
import getpass import getpass
from gatehouse_app.models.authentication_method import ApplicationProviderConfig from gatehouse_app.models.auth.authentication_method import ApplicationProviderConfig
from gatehouse_app.extensions import db from gatehouse_app.extensions import db
SUPPORTED = ["google", "github", "microsoft"] SUPPORTED = ["google", "github", "microsoft"]
print("=" * 60) # Well-known endpoints — stored in additional_config so the adapter can
print("OAuth Provider Configuration") # resolve auth_url / token_url / userinfo_url without extra logic.
print("=" * 60) PROVIDER_DEFAULTS = {
print(f"Supported providers: {', '.join(SUPPORTED)}") "google": {
"auth_url": "https://accounts.google.com/o/oauth2/v2/auth",
"token_url": "https://oauth2.googleapis.com/token",
"userinfo_url": "https://www.googleapis.com/oauth2/v3/userinfo",
},
"github": {
"auth_url": "https://github.com/login/oauth/authorize",
"token_url": "https://github.com/login/oauth/access_token",
"userinfo_url": "https://api.github.com/user",
},
"microsoft": {
"auth_url": "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
"token_url": "https://login.microsoftonline.com/common/oauth2/v2.0/token",
"userinfo_url": "https://graph.microsoft.com/oidc/userinfo",
},
}
provider = input("Provider [google/github/microsoft]: ").strip().lower() if not provider:
print("=" * 60)
print("OAuth Provider Configuration")
print("=" * 60)
print(f"Supported providers: {', '.join(SUPPORTED)}")
provider = input("Provider [google/github/microsoft]: ").strip().lower()
provider = provider.strip().lower()
if provider not in SUPPORTED: if provider not in SUPPORTED:
print(f"❌ Unknown provider: {provider}") print(f"❌ Unknown provider: {provider}")
return return
client_id = input("Client ID: ").strip() if not client_id:
client_id = input("Client ID: ").strip()
if not client_id: if not client_id:
print("❌ client_id is required") print("❌ client_id is required")
return return
client_secret = getpass.getpass("Client Secret (leave blank to keep existing): ").strip() if not client_secret:
client_secret = getpass.getpass("Client Secret (leave blank to keep existing): ").strip()
if not redirect_url:
base_url = os.getenv("API_BASE_URL", "http://localhost:5000/api/v1")
default = f"{base_url}/auth/external/{provider}/callback"
entered = input(f"Default redirect URL [{default}]: ").strip()
redirect_url = entered or default
additional_config = PROVIDER_DEFAULTS[provider].copy()
with app.app_context(): with app.app_context():
config = ApplicationProviderConfig.query.filter_by(provider_type=provider).first() config = ApplicationProviderConfig.query.filter_by(provider_type=provider).first()
@@ -191,6 +231,11 @@ def configure_oauth():
if client_secret: if client_secret:
config.set_client_secret(client_secret) config.set_client_secret(client_secret)
config.is_enabled = True config.is_enabled = True
config.default_redirect_url = redirect_url
config.additional_config = {
**(config.additional_config or {}),
**additional_config,
}
db.session.commit() db.session.commit()
print(f"✅ Updated {provider} provider config.") print(f"✅ Updated {provider} provider config.")
else: else:
@@ -198,12 +243,16 @@ def configure_oauth():
provider_type=provider, provider_type=provider,
client_id=client_id, client_id=client_id,
is_enabled=True, is_enabled=True,
default_redirect_url=redirect_url,
additional_config=additional_config,
) )
if client_secret: if client_secret:
config.set_client_secret(client_secret) config.set_client_secret(client_secret)
db.session.add(config) db.session.add(config)
db.session.commit() db.session.commit()
print(f"✅ Created {provider} provider config.") print(f"✅ Created {provider} provider config.")
print(f" redirect_url : {redirect_url}")
print(f" auth_url : {additional_config['auth_url']}")
@cli.command("list_oauth") @cli.command("list_oauth")
@@ -213,7 +262,7 @@ def list_oauth():
Usage: Usage:
python manage.py list_oauth python manage.py list_oauth
""" """
from gatehouse_app.models.authentication_method import ApplicationProviderConfig from gatehouse_app.models.auth.authentication_method import ApplicationProviderConfig
with app.app_context(): with app.app_context():
configs = ApplicationProviderConfig.query.all() configs = ApplicationProviderConfig.query.all()
+1 -1
View File
@@ -58,7 +58,7 @@ if os.path.exists(env_file):
# Import after path setup # Import after path setup
from gatehouse_app import create_app from gatehouse_app import create_app
from gatehouse_app.services.external_auth_service import ExternalAuthService, ExternalAuthError from gatehouse_app.services.external_auth import ExternalAuthService, ExternalAuthError
def _microsoft_defaults() -> dict: def _microsoft_defaults() -> dict:
+7
View File
@@ -1,4 +1,11 @@
"""Initialize database script.""" """Initialize database script."""
import sys
import os
import time
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent))
from gatehouse_app import create_app from gatehouse_app import create_app
from gatehouse_app.extensions import db from gatehouse_app.extensions import db
from sqlalchemy import text from sqlalchemy import text