75 lines
2.7 KiB
Python
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)
|