Cookie Security Design
Overview
This document explains the design decisions behind the session cookie security implementation in the MCP Gateway Registry, particularly regarding the use of domain cookies for cross-subdomain authentication.
Background
The MCP Gateway Registry supports authentication through both traditional username/password and OAuth2 providers. In deployments where the auth server and registry are on different subdomains (e.g., auth.example.com and registry.example.com), session cookies must be shared across these subdomains for seamless authentication.
Design Decision: Single-Tenant Architecture
This implementation is designed for single-tenant deployments where:
- All subdomains are owned and controlled by a single organization
- Cross-subdomain cookie sharing is a desired feature, not a security risk
- Users authenticate once and access multiple services on different subdomains
Cookie Security Configuration
Environment Variables
Two key environment variables control cookie security behavior:
SESSION_COOKIE_SECURE(default:false)- Set to
truein production deployments with HTTPS - When
true, cookies are only transmitted over HTTPS connections - Prevents man-in-the-middle (MITM) attacks and session hijacking
- Production Requirement: MUST be set to
truewhen deployed with HTTPS
- Set to
SESSION_COOKIE_DOMAIN(default:Noneor empty string)- MUST be explicitly configured - no automatic domain inference
- When set (e.g.,
.example.com), enables cross-subdomain cookie sharing - Must start with a dot (
.) to match all subdomains - When
Noneor empty, cookies are scoped to the exact host that sets them - Format:
.example.com(note the leading dot) - Important: Set to empty string
""for single-domain deployments - Examples:
- Single domain (
mcpgateway.ddns.net): Leave unset or set to"" - Cross-subdomain (
auth.example.com,registry.example.com): Set to.example.com - Multi-level domains (
registry.region-1.corp.company.internal): Set to.corp.company.internalif cross-subdomain sharing needed
- Single domain (
HTTPS Termination Detection
Critical Implementation Detail: The auth server intelligently handles HTTPS termination at load balancers (ALB, nginx, etc.):
- Backend sees HTTP but load balancer terminates HTTPS → Common in AWS ALB, nginx reverse proxy
- Solution: Auth server checks
X-Forwarded-Protoheader to detect original protocol - Behavior: Cookie
secureflag is set based on original request protocol, not backend protocol
Code Logic (auth_server/server.py:1797-1803):
x_forwarded_proto = request.headers.get("x-forwarded-proto", "")
is_https = x_forwarded_proto == "https" or request.url.scheme == "https"
# Only set secure=True if the original request was HTTPS
cookie_secure_config = OAUTH2_CONFIG.get("session", {}).get("secure", False)
cookie_secure = cookie_secure_config and is_https
Important:
- If
SESSION_COOKIE_SECURE=truebutis_https=False, the secure flag will NOT be set - This prevents login failures when HTTPS termination is misconfigured
- Check server logs for
is_https=Truein production to verify HTTPS detection is working
Cookie Security Flags
The implementation sets the following security flags on all session cookies:
| Flag | Value | Purpose |
|---|---|---|
httponly |
True |
Prevents JavaScript access, mitigating XSS attacks |
samesite |
"lax" |
Provides CSRF protection while allowing cross-site navigation |
secure |
Configurable | Ensures HTTPS-only transmission in production |
path |
"/" |
Explicitly scopes cookie to entire domain |
domain |
Configurable | Enables cross-subdomain sharing when needed |
Security Considerations
✅ Safe Deployment Scenarios
This design is SAFE for:
Single-Tenant Production Deployments
- Example:
auth.company.comandregistry.company.com - All subdomains owned by the same organization
- Configuration:
SESSION_COOKIE_SECURE=true SESSION_COOKIE_DOMAIN=.company.com
- Example:
Local Development (localhost)
- Local development on
localhostvia HTTP - Configuration:
SESSION_COOKIE_SECURE=false # MUST be false for HTTP SESSION_COOKIE_DOMAIN= # Leave unset/empty - Important: Setting
SESSION_COOKIE_SECURE=trueon localhost will cause login to fail because cookies withsecure=trueare only sent over HTTPS, and localhost typically runs over HTTP.
- Local development on
⚠️ Unsafe Deployment Scenarios
This design is NOT SAFE for:
Multi-Tenant SaaS Deployments
- Example:
customer1.saas-platform.comandcustomer2.saas-platform.com - Risk: Setting
SESSION_COOKIE_DOMAIN=.saas-platform.comwould allow:- Customer A to access Customer B's sessions
- Cross-tenant authentication bypass
- Serious data breach potential
- Example:
Shared Hosting Environments
- Multiple organizations sharing the same root domain
- Risk: Similar to multi-tenant scenario
Alternative Solutions for Multi-Tenant
If you need multi-tenant deployment, consider these alternatives:
Token-Based Authentication
- Use JWT tokens passed via headers instead of cookies
- Tokens explicitly scoped to each tenant
- No domain-sharing concerns
Separate Auth Domains per Tenant
customer1-auth.platform.comandcustomer1-app.platform.com- Different root domains prevent cookie sharing between tenants
Reverse Proxy with Path-Based Routing
- Single domain with path-based service routing
- Example:
platform.com/authandplatform.com/registry - No cross-subdomain cookie requirements
Centralized OAuth Flow
- OAuth server on separate domain
- Token exchange instead of session cookies
- Better tenant isolation
Attack Scenarios Mitigated
1. Session Hijacking (MITM)
- Threat: Attacker intercepts session cookies over unencrypted HTTP
- Mitigation:
secure=Trueflag in production - Status: ✅ Mitigated when
SESSION_COOKIE_SECURE=true
2. Cross-Site Scripting (XSS)
- Threat: Malicious JavaScript reads session cookies
- Mitigation:
httponly=Trueflag - Status: ✅ Always mitigated
3. Cross-Site Request Forgery (CSRF)
- Threat: Malicious site triggers authenticated requests
- Mitigation:
samesite="lax"flag - Status: ✅ Always mitigated
4. Subdomain Cookie Theft (Single-Tenant)
- Threat: Attacker controls a subdomain and steals cookies
- Mitigation: Only valid in trusted single-tenant environments
- Status: ⚠️ Acceptable risk for single-tenant deployments
Production Deployment Checklist
Before deploying to production:
- Set
SESSION_COOKIE_SECURE=truein environment (REQUIRED for HTTPS) - Verify HTTPS is properly configured and enforced
- IMPORTANT: If using load balancer with HTTPS termination, ensure
X-Forwarded-Protoheader is set - Set
SESSION_COOKIE_DOMAINappropriately:- Empty string or unset for single-domain deployments (RECOMMENDED - safest)
.example.comonly if you need cross-subdomain authentication
- Confirm you are deploying in a single-tenant architecture (NOT multi-tenant SaaS)
- Test cross-subdomain authentication between auth and registry services (if using domain cookies)
- Verify cookies are NOT transmitted over HTTP in production
- Review server logs for cookie configuration at startup:
- Check for
Auth server setting session cookie: secure=True - Verify
domainsetting matches your configuration - Confirm
is_https=Truein production
- Check for
Example Production Configurations
Single-Domain Deployment (RECOMMENDED - Most Secure):
# .env for production - single domain (e.g., mcpgateway.example.com)
SESSION_COOKIE_SECURE=true # REQUIRED for HTTPS
SESSION_COOKIE_DOMAIN= # Empty = exact host only (safest)
SESSION_COOKIE_NAME=mcp_gateway_session
SESSION_MAX_AGE_SECONDS=28800 # 8 hours
AUTH_SERVER_URL=http://auth-server:8888 # Internal URL
AUTH_SERVER_EXTERNAL_URL=https://mcpgateway.example.com # External URL
Cross-Subdomain Deployment:
# .env for production - cross-subdomain (e.g., auth.example.com + registry.example.com)
SESSION_COOKIE_SECURE=true # REQUIRED for HTTPS
SESSION_COOKIE_DOMAIN=.example.com # Note the leading dot
SESSION_COOKIE_NAME=mcp_gateway_session
SESSION_MAX_AGE_SECONDS=28800 # 8 hours
AUTH_SERVER_URL=http://auth-server:8888 # Internal URL
AUTH_SERVER_EXTERNAL_URL=https://auth.example.com # External URL
Code Implementation
The cookie security implementation is found in:
Configuration:
registry/core/config.pysession_cookie_secure: Controls HTTPS-only flagsession_cookie_domain: Controls cross-subdomain sharing
Auth Server Cookie Setting:
auth_server/server.py(lines 1800-1831)- X-Forwarded-Proto detection for HTTPS termination at load balancer
- Explicit configuration only - no automatic domain inference
- Conditional secure flag based on both config AND actual protocol
- All security flags properly set
Registry Cookie Setting:
registry/auth/routes.py(lines 139-158)- Comprehensive security comments explaining single-tenant model
- Conditional domain attribute application
- All security flags properly set
Monitoring and Validation
Runtime Validation
The auth server logs detailed cookie configuration for debugging:
logger.info(f"Auth server setting session cookie: secure={cookie_secure} (config={cookie_secure_config}, is_https={is_https}), samesite={cookie_samesite}, domain={cookie_domain or 'not set'}, x-forwarded-proto={x_forwarded_proto}, request_scheme={request.url.scheme}")
Key logging details:
- secure: Final secure flag value (after protocol detection)
- config: Configured SESSION_COOKIE_SECURE value
- is_https: Whether the original request was HTTPS (based on X-Forwarded-Proto or request scheme)
- domain: Configured domain or "not set"
- x-forwarded-proto: Load balancer protocol header
- request_scheme: Direct request protocol
The registry logs successful login events:
logger.info(f"User '{username}' logged in successfully.")
Security Auditing
Periodically review:
- Cookie flags are properly set in browser developer tools
- Cookies are NOT transmitted over HTTP in production
secureflag is enabled in production environments- Domain scope matches your deployment architecture
Browser Developer Tools Verification
In your browser's developer tools (Application/Storage → Cookies), verify:
| Property | Expected Value | Notes |
|---|---|---|
Secure |
✓ (checked) | Production only |
HttpOnly |
✓ (checked) | Always |
SameSite |
Lax |
Always |
Domain |
.example.com |
If configured |
Path |
/ |
Always |
References
- OWASP Session Management Cheat Sheet
- MDN: Set-Cookie HTTP Header
- RFC 6265: HTTP State Management Mechanism
Contact
For questions or security concerns regarding this implementation, please:
- Open an issue in the GitHub repository
- Tag the issue with
securitylabel - Provide details about your deployment scenario