feat(oidc): add debug logging and migrate client secret hashing to Flask-Bcrypt

- Add comprehensive debug logging across OIDC endpoints and services for development troubleshooting
- Implement backward-compatible password hash checking with automatic migration from raw bcrypt to Flask-Bcrypt format
- Refactor logging configuration to ensure proper propagation across all app modules
- Configure root logger and disable Werkzeug duplication for cleaner log output
- Initialize OIDC JWKS service on application startup
- Update seed script to use Flask-Bcrypt for client secret hashing
- Fix audit service to use correct event_metadata parameter

BREAKING CHANGE: Client secrets created with old raw bcrypt format will be automatically migrated to Flask-Bcrypt format on first successful authentication
This commit is contained in:
2026-01-09 12:59:53 +10:30
parent 5e060f267d
commit a6474f55c1
8 changed files with 348 additions and 21 deletions
+52 -3
View File
@@ -1,12 +1,18 @@
"""Application factory."""
import os
import logging
# Test debug logging - this should appear when running `flask run --debug`
_root_logger = logging.getLogger(__name__)
_root_logger.debug("[TEST] Debug logging is working!")
from flask import Flask
from config import get_config
from app.extensions import db, migrate, bcrypt, ma, limiter, session
from app.middleware import RequestIDMiddleware, SecurityHeadersMiddleware, setup_cors
from app.exceptions.base import BaseAPIException
from app.utils.response import api_response
from app.services.oidc_jwks_service import OIDCJWKSService
import redis
# Configure SQLAlchemy logging BEFORE any database operations
@@ -50,6 +56,9 @@ def create_app(config_name=None):
# Setup logging
setup_logging(app)
# Initialize OIDC JWKS service with a signing key
initialize_oidc_jwks(app)
return app
@@ -167,16 +176,32 @@ def setup_logging(app):
# Create formatter
formatter = logging.Formatter(
"[%(asctime)s] %(levelname)s in %(module)s: %(message)s"
"[%(asctime)s] [%(levelname)s] %(name)s: %(message)s"
)
# Configure root logger
# Configure root logger - this ensures all module loggers (like app.services.oidc_service)
# will output DEBUG level logs when in development mode
root_logger = logging.getLogger()
root_logger.setLevel(log_level)
if app.config.get("LOG_TO_STDOUT"):
stream_handler = logging.StreamHandler()
stream_handler.setFormatter(formatter)
stream_handler.setLevel(log_level)
app.logger.addHandler(stream_handler)
root_logger.addHandler(stream_handler)
# Disable Werkzeug's default logger to avoid log duplication and interference
werkzeug_logger = logging.getLogger('werkzeug')
werkzeug_logger.setLevel(logging.INFO)
# Ensure child loggers propagate to root logger
# This is the key fix - explicitly enable propagation for common app loggers
for logger_name in ['app', 'app.api', 'app.api.oidc', 'app.services', 'app.models']:
child_logger = logging.getLogger(logger_name)
child_logger.propagate = True
child_logger.setLevel(log_level)
# Configure Flask app logger
app.logger.setLevel(log_level)
# Configure SQLAlchemy logging level (also set at module level before DB init)
@@ -187,3 +212,27 @@ def setup_logging(app):
logging.getLogger('sqlalchemy.pool').setLevel(sqlalchemy_log_level)
app.logger.info("Application startup")
# Test debug log after logging is configured
app.logger.debug("[TEST] Debug logging is working!")
def initialize_oidc_jwks(app):
"""Initialize OIDC JWKS service with a signing key.
This ensures that signing keys are available for token generation.
Args:
app: Flask application instance
"""
with app.app_context():
try:
jwks_service = OIDCJWKSService()
signing_key = jwks_service.get_signing_key()
if not signing_key:
signing_key = jwks_service.initialize_with_key()
app.logger.info(f"[OIDC] Generated new signing key: kid={signing_key.kid}")
else:
app.logger.info(f"[OIDC] Using existing signing key: kid={signing_key.kid}")
except Exception as e:
app.logger.error(f"[OIDC] Failed to initialize JWKS: {e}")