From a7e62a1ce4e694ef0d9b9faef8f6f222bbf7f352 Mon Sep 17 00:00:00 2001 From: Usama Sadiq Date: Tue, 7 Jul 2026 23:23:00 +0500 Subject: [PATCH] Add corporate_id check to ProxyAuth middleware --- .../authentication/middleware/proxy_auth.py | 28 +++++++++++++++++++ apps/api/plane/settings/common.py | 1 + 2 files changed, 29 insertions(+) diff --git a/apps/api/plane/authentication/middleware/proxy_auth.py b/apps/api/plane/authentication/middleware/proxy_auth.py index 4d19a5dfa8b..4dce5939790 100644 --- a/apps/api/plane/authentication/middleware/proxy_auth.py +++ b/apps/api/plane/authentication/middleware/proxy_auth.py @@ -4,10 +4,12 @@ from uuid import uuid4 +import jwt from django.conf import settings from django.contrib.auth import logout from django.contrib.auth.hashers import make_password from django.db import IntegrityError +from django.http import JsonResponse from plane.authentication.middleware.proxy_auth_utils import ( _coerce_bypass_paths, @@ -36,6 +38,29 @@ } +def _check_corporate_id(request) -> bool: + """Verify the caller's access token contains a corporate_id matching this deployment. + + When SMB_CORPORATE_ID is configured, only corporate mPass tokens whose + ``custom:corporate_id`` claim matches are allowed through. Individual + (non-corporate) tokens are rejected. When SMB_CORPORATE_ID is not set, + the check is skipped entirely for backward compatibility. + """ + expected = getattr(settings, "SMB_CORPORATE_ID", None) + if not expected: + return True + access_token = request.META.get("HTTP_X_AUTH_REQUEST_ACCESS_TOKEN") + if not access_token: + return False + try: + claims = jwt.decode(access_token, options={"verify_signature": False}) + except Exception: + return False + if claims.get("custom:is_corporate") != "true": + return False + return claims.get("custom:corporate_id") == expected + + class ProxyAuthMiddleware: """ Django middleware for mPass proxy authentication. @@ -82,6 +107,9 @@ def __call__(self, request): if not email: return self.get_response(request) + if not _check_corporate_id(request): + return JsonResponse({"error": "access_denied"}, status=403) + user = self._resolve_user(email) # Respect deactivated accounts — mPass authentication does not diff --git a/apps/api/plane/settings/common.py b/apps/api/plane/settings/common.py index 6713c9340e2..7a5446b3799 100644 --- a/apps/api/plane/settings/common.py +++ b/apps/api/plane/settings/common.py @@ -106,6 +106,7 @@ # SMB portal hostname segment (landing / logout redirects) vs default Plane workspace slug SMB_NAME = os.environ.get("SMB_NAME") SMB_DEFAULT_WORKSPACE_NAME = os.environ.get("SMB_DEFAULT_WORKSPACE_NAME") or SMB_NAME +SMB_CORPORATE_ID = os.environ.get("SMB_CORPORATE_ID") # Middlewares MIDDLEWARE = [