""" OAuth2 Bearer Token Authentication Middleware for CMS. The Studio MFE (course-authoring MFE) authenticates API calls to CMS using OAuth2 Bearer tokens obtained from the LMS during login. When the user logs in via the authn MFE: 1. LMS sets a Django session cookie 2. LMS returns an OAuth2 access token 3. The MFE stores the access token in localStorage On every API request to CMS, the MFE's Axios sends: Authorization: Bearer This middleware intercepts those requests, validates the Bearer token against the LMS userinfo endpoint, and attaches the Django user to the request. """ import logging logger = logging.getLogger(__name__) class CMSOAuth2BearerAuthMiddleware: """ Middleware that authenticates CMS API requests using OAuth2 Bearer tokens. The Studio MFE (course-authoring MFE) stores an OAuth2 access token obtained from LMS login in localStorage, and sends it as: Authorization: Bearer on every API request to CMS. This middleware validates the token against the LMS userinfo endpoint and attaches the authenticated Django user to the request. """ def __init__(self, get_response): self.get_response = get_response self.lms_userinfo_url = "http://lms:8000/oauth2/user_info" def __call__(self, request): # Check for Bearer token in Authorization header auth_header = request.META.get('HTTP_AUTHORIZATION', '') logger.debug( "CMSOAuth2BearerAuth: path=%s auth_header=%r", request.path, auth_header[:30] if auth_header else None ) if auth_header.startswith('Bearer '): token = auth_header[7:] if token: user = self._validate_token_get_user(token) if user: # Attach the user to the request # This makes request.user available for Django's auth checks request.user = user # Cache the user to prevent re-evaluation request._cached_user = user logger.debug( "CMSOAuth2BearerAuth: token validated for user=%s (id=%s)", user.username, user.id ) else: logger.debug("OAuth2 Bearer token validation failed") else: # No Bearer token - let session auth handle it pass return self.get_response(request) def _validate_token_get_user(self, token): """Call LMS userinfo endpoint and return Django User.""" import requests try: resp = requests.get( self.lms_userinfo_url, headers={'Authorization': f'Bearer {token}'}, timeout=5, ) if resp.status_code != 200: logger.debug( "LMS userinfo returned %s for token validation", resp.status_code ) return None user_info = resp.json() username = user_info.get('username') if not username: logger.debug("No username in LMS userinfo response") return None # Get the Django User object from django.contrib.auth import get_user_model User = get_user_model() try: user = User.objects.get(username=username) logger.debug("Found Django user: %s (id=%s)", username, user.id) return user except User.DoesNotExist: logger.debug("Django user not found: %s", username) return None except Exception as e: logger.debug("Error validating OAuth2 token: %s", e) return None