Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions auth_server/oauth2_providers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
48 changes: 40 additions & 8 deletions auth_server/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
Loading