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
+24 -1
View File
@@ -1,5 +1,7 @@
"""Organization service."""
import logging
from datetime import datetime
from flask import current_app
from app.extensions import db
from app.models.organization import Organization
from app.models.organization_member import OrganizationMember
@@ -7,6 +9,8 @@ from app.exceptions.validation_exceptions import OrganizationNotFoundError, Conf
from app.utils.constants import OrganizationRole, AuditAction
from app.services.audit_service import AuditService
logger = logging.getLogger(__name__)
class OrganizationService:
"""Service for organization operations."""
@@ -80,6 +84,11 @@ class OrganizationService:
OrganizationNotFoundError: If organization not found
"""
org = Organization.query.filter_by(id=org_id, deleted_at=None).first()
# Development-only debug logging for organization validation
if current_app.config.get('ENV') == 'development':
logger.debug(f"[Org] Get organization by ID: org_id={org_id}, exists={org is not None}")
if not org:
raise OrganizationNotFoundError()
return org
@@ -95,7 +104,13 @@ class OrganizationService:
Returns:
Organization instance or None
"""
return Organization.query.filter_by(slug=slug, deleted_at=None).first()
org = Organization.query.filter_by(slug=slug, deleted_at=None).first()
# Development-only debug logging for organization validation
if current_app.config.get('ENV') == 'development':
logger.debug(f"[Org] Get organization by slug: slug={slug}, exists={org is not None}")
return org
@staticmethod
def update_organization(org, user_id, **kwargs):
@@ -180,6 +195,10 @@ class OrganizationService:
deleted_at=None,
).first()
# Development-only debug logging for membership validation
if current_app.config.get('ENV') == 'development':
logger.debug(f"[Org] Member check: org_id={org.id}, user_id={user_id}, already_member={existing is not None}")
if existing:
raise ConflictError("User is already a member of this organization")
@@ -223,6 +242,10 @@ class OrganizationService:
deleted_at=None,
).first()
# Development-only debug logging for membership removal validation
if current_app.config.get('ENV') == 'development':
logger.debug(f"[Org] Member removal: org_id={org.id}, user_id={user_id}, found={member is not None}")
if member:
member.delete(soft=True)