From 7ee289e2e74cd8316c73758f983f40eaf62691da Mon Sep 17 00:00:00 2001 From: Justin Bowen Date: Mon, 13 Jul 2026 20:09:15 -0500 Subject: [PATCH] fix(auth): require JWT_SECRET_KEY in prod + run config validation at startup Fixes two critical findings: - C1: JWT_SECRET_KEY no longer falls back to the dev SECRET_KEY default in production. ProductionConfig now requires it via env var and validate() rejects the dev default, closing an admin-token forgery vector. - C2: create_app() now calls ProductionConfig.validate() at startup (guarded by hasattr so Dev/Testing configs are unaffected), so the dead secret-guard actually runs and the app boot-fails on default/missing secrets. Co-Authored-By: Claude Fable 5 --- services/flask-backend/app/__init__.py | 7 +++++++ services/flask-backend/app/config.py | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/services/flask-backend/app/__init__.py b/services/flask-backend/app/__init__.py index 1587bf41..17140260 100644 --- a/services/flask-backend/app/__init__.py +++ b/services/flask-backend/app/__init__.py @@ -38,6 +38,9 @@ def create_app(config_class: type | None = None) -> Quart: Returns: Configured Quart application instance + + Raises: + ValueError: If production config validation fails (missing or invalid secrets) """ app = Quart(__name__) @@ -46,6 +49,10 @@ def create_app(config_class: type | None = None) -> Quart: config_class = get_config() app.config.from_object(config_class) + # Validate configuration (production only) + if hasattr(config_class, "validate"): + config_class.validate() + # Setup logging _setup_logging(app) diff --git a/services/flask-backend/app/config.py b/services/flask-backend/app/config.py index 323de924..cf865bcd 100644 --- a/services/flask-backend/app/config.py +++ b/services/flask-backend/app/config.py @@ -177,6 +177,7 @@ class ProductionConfig(Config): # Strict security in production SECURITY_PASSWORD_SALT = os.getenv("SECURITY_PASSWORD_SALT") # Required SECRET_KEY = os.getenv("SECRET_KEY") # Required + JWT_SECRET_KEY = os.getenv("JWT_SECRET_KEY") # Required — must not fall back to dev default @classmethod def validate(cls) -> None: @@ -188,6 +189,11 @@ def validate(cls) -> None: raise ValueError("SECRET_KEY must be set in production") if not cls.SECURITY_PASSWORD_SALT or "change" in cls.SECURITY_PASSWORD_SALT: raise ValueError("SECURITY_PASSWORD_SALT must be set in production") + if ( + not cls.JWT_SECRET_KEY + or cls.JWT_SECRET_KEY == "dev-secret-key-change-in-production" + ): + raise ValueError("JWT_SECRET_KEY must be set in production") class TestingConfig(Config):