Files
billing/billing_app/graphql_middleware.py
2026-01-07 09:17:45 +07:00

75 lines
2.7 KiB
Python

"""
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)