Initial commit from monorepo
This commit is contained in:
74
billing_app/graphql_middleware.py
Normal file
74
billing_app/graphql_middleware.py
Normal file
@@ -0,0 +1,74 @@
|
||||
"""
|
||||
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)
|
||||
Reference in New Issue
Block a user