""" GraphQL middleware for JWT authentication and scope checking in the Billing service. This middleware runs for every GraphQL resolver and sets user_id and scopes on info.context based on the JWT token in Authorization header. """ import logging from django.conf import settings from graphql import GraphQLError from jwt import InvalidTokenError from .auth import get_bearer_token, scopes_from_payload, validator def _is_introspection(info) -> bool: """Возвращает True для любых introspection резолвов.""" field = getattr(info, "field_name", "") parent = getattr(getattr(info, "parent_type", None), "name", "") return field.startswith("__") or parent.startswith("__") class PublicNoAuthMiddleware: """Public endpoint - no authentication required.""" def resolve(self, next, root, info, **kwargs): return next(root, info, **kwargs) class M2MNoAuthMiddleware: """M2M endpoint - internal services only, no auth for now.""" def resolve(self, next, root, info, **kwargs): return next(root, info, **kwargs) class BillingJWTMiddleware: """ Middleware for Billing service operations. Sets info.context.user_id and scopes from the JWT access token. """ def resolve(self, next, root, info, **kwargs): request = info.context if _is_introspection(info): return next(root, info, **kwargs) # Skip if JWT processing already happened (e.g., another middleware already set it) if hasattr(request, '_billing_jwt_processed') and request._billing_jwt_processed: return next(root, info, **kwargs) request._billing_jwt_processed = True try: token = get_bearer_token(request) payload = validator.decode( token, audience=getattr(settings, 'LOGTO_BILLING_AUDIENCE', None), ) request.user_id = payload.get('sub') # Subject (user ID) request.team_uuid = payload.get('team_uuid') request.scopes = scopes_from_payload(payload) if not request.team_uuid or 'teams:member' not in request.scopes: raise GraphQLError("Unauthorized") logging.debug(f"JWT processed for user_id: {request.user_id}, scopes: {request.scopes}") except InvalidTokenError as exc: logging.info(f"Billing JWT authentication failed: {exc}") raise GraphQLError("Unauthorized") from exc except Exception as exc: logging.error(f"An unexpected error occurred during JWT processing: {exc}") raise GraphQLError("Unauthorized") from exc return next(root, info, **kwargs)