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
+28 -7
View File
@@ -1,6 +1,8 @@
"""Authentication service."""
import logging
import secrets
from datetime import datetime, timedelta
from flask import request, g
from flask import request, g, current_app
from app.extensions import db, bcrypt
from app.models.user import User
from app.models.authentication_method import AuthenticationMethod
@@ -9,7 +11,8 @@ from app.utils.constants import AuthMethodType, SessionStatus, UserStatus, Audit
from app.exceptions.auth_exceptions import InvalidCredentialsError, AccountSuspendedError, AccountInactiveError
from app.exceptions.validation_exceptions import EmailAlreadyExistsError
from app.services.audit_service import AuditService
import secrets
logger = logging.getLogger(__name__)
class AuthService:
@@ -85,27 +88,45 @@ class AuthService:
"""
# Find user
user = User.query.filter_by(email=email.lower(), deleted_at=None).first()
# Development-only debug logging for user existence check
if current_app.config.get('ENV') == 'development':
logger.debug(f"[Auth] User lookup: email={email}, exists={user is not None}")
if not user:
raise InvalidCredentialsError()
# Check account status
if current_app.config.get('ENV') == 'development':
logger.debug(f"[Auth] Account status: user_id={user.id}, status={user.status}")
if user.status == UserStatus.SUSPENDED:
raise AccountSuspendedError()
if user.status == UserStatus.INACTIVE:
raise AccountInactiveError()
# Find password auth method
auth_method = AuthenticationMethod.query.filter_by(
user_id=user.id,
method_type=AuthMethodType.PASSWORD,
deleted_at=None,
).first()
# Development-only debug logging for auth method lookup
if current_app.config.get('ENV') == 'development':
logger.debug(f"[Auth] Auth method lookup: user_id={user.id}, has_password_auth={auth_method is not None and auth_method.password_hash is not None}")
if not auth_method or not auth_method.password_hash:
raise InvalidCredentialsError()
# Verify password
if not bcrypt.check_password_hash(auth_method.password_hash, password):
password_valid = bcrypt.check_password_hash(auth_method.password_hash, password)
# Development-only debug logging for password validation (without logging actual password)
if current_app.config.get('ENV') == 'development':
logger.debug(f"[Auth] Password validation: user_id={user.id}, valid={password_valid}")
if not password_valid:
raise InvalidCredentialsError()
# Update last login