diff --git a/auth_server/oauth2_providers.yml b/auth_server/oauth2_providers.yml index 0ff84e9..09cc020 100644 --- a/auth_server/oauth2_providers.yml +++ b/auth_server/oauth2_providers.yml @@ -96,6 +96,7 @@ session: secure: true # Set to false for development httponly: true samesite: "lax" + domain: "${SESSION_COOKIE_DOMAIN}" # Set to your domain (with leading dot) to share cookies across subdomains, e.g., ".example.com" # Registry integration settings registry: diff --git a/auth_server/server.py b/auth_server/server.py index a19fef0..94f6497 100644 --- a/auth_server/server.py +++ b/auth_server/server.py @@ -1794,14 +1794,46 @@ async def oauth2_callback( response = RedirectResponse(url=redirect_url, status_code=302) # Set registry-compatible session cookie - response.set_cookie( - key="mcp_gateway_session", # Same as registry SESSION_COOKIE_NAME - value=registry_session, - max_age=OAUTH2_CONFIG.get("session", {}).get("max_age_seconds", 28800), - httponly=OAUTH2_CONFIG.get("session", {}).get("httponly", True), - samesite=OAUTH2_CONFIG.get("session", {}).get("samesite", "lax"), - secure=OAUTH2_CONFIG.get("session", {}).get("secure", False) - ) + cookie_secure = OAUTH2_CONFIG.get("session", {}).get("secure", False) + cookie_samesite = OAUTH2_CONFIG.get("session", {}).get("samesite", "lax") + cookie_domain = OAUTH2_CONFIG.get("session", {}).get("domain", "") + + # Auto-infer domain from request if not explicitly configured + if not cookie_domain or cookie_domain == "${SESSION_COOKIE_DOMAIN}": + # Get the host from headers (ALB sets x-forwarded-host or use Host header) + host = request.headers.get("x-forwarded-host") or request.headers.get("host", "") + + # Extract base domain for cross-subdomain cookies + # e.g., "auth-server.subdomain.domain.tld" -> ".domain.tld" + if host and "." in host: + # Remove port if present + host = host.split(":")[0] + + # Split by dots and take last 2 parts for base domain + parts = host.split(".") + if len(parts) >= 2: + # Skip if it's localhost or an IP address + if not host.startswith("localhost") and not host.replace(".", "").isdigit(): + cookie_domain = f".{'.'.join(parts[-2:])}" + logger.info(f"Auto-inferred cookie domain from host '{host}': {cookie_domain}") + + logger.info(f"Auth server setting session cookie: secure={cookie_secure}, samesite={cookie_samesite}, domain={cookie_domain or 'not set'}, request_scheme={request.url.scheme}") + + cookie_params = { + "key": "mcp_gateway_session", # Same as registry SESSION_COOKIE_NAME + "value": registry_session, + "max_age": OAUTH2_CONFIG.get("session", {}).get("max_age_seconds", 28800), + "httponly": OAUTH2_CONFIG.get("session", {}).get("httponly", True), + "samesite": cookie_samesite, + "secure": cookie_secure, + "path": "/" # Ensure cookie is sent for all paths + } + + # Only set domain if configured or inferred (for cross-subdomain cookies) + if cookie_domain: + cookie_params["domain"] = cookie_domain + + response.set_cookie(**cookie_params) # Clear temporary OAuth2 session response.delete_cookie("oauth2_temp_session")