diff --git a/.env.example b/.env.example index c2c1f484..c9161ddd 100644 --- a/.env.example +++ b/.env.example @@ -65,9 +65,6 @@ REQUIRE_HTTPS=false # Restrict allowed hosts (comma-separated) # ALLOWED_HOSTS=localhost,yourdomain.com -# Session secret (use a secure random string in production) -SESSION_SECRET=dev-session-secret-change-in-production - # ===== OAuth Dynamic Client Registration (DCR) Configuration ===== # Storage backend: auto (default), memory, file, redis # DCR_STORE_TYPE=auto diff --git a/docs/api.md b/docs/api.md index 06aa77db..4a77569d 100644 --- a/docs/api.md +++ b/docs/api.md @@ -301,7 +301,6 @@ The following environment variables affect API behavior: - `MICROSOFT_CLIENT_ID`, `MICROSOFT_CLIENT_SECRET` for Microsoft OAuth - `ALLOWED_ORIGINS`: Comma-separated list of allowed CORS origins - `ALLOWED_HOSTS`: Comma-separated list of allowed hosts -- `SESSION_SECRET`: Secret key for session management ## Security Considerations diff --git a/docs/dual-mode-guide.md b/docs/dual-mode-guide.md index 30c85d18..195d186c 100644 --- a/docs/dual-mode-guide.md +++ b/docs/dual-mode-guide.md @@ -193,7 +193,6 @@ EXPOSE 8080 - [ ] Configure real Google OAuth credentials - [ ] Set `REQUIRE_HTTPS=true` - [ ] Configure `ALLOWED_ORIGINS` -- [ ] Set secure `SESSION_SECRET` - [ ] Configure SSL/TLS certificates - [ ] Set up reverse proxy (nginx, etc.) - [ ] Configure monitoring and logging diff --git a/docs/oauth-setup.md b/docs/oauth-setup.md index 51758e09..a475ddc9 100644 --- a/docs/oauth-setup.md +++ b/docs/oauth-setup.md @@ -403,7 +403,6 @@ export NODE_ENV=production export MCP_MODE=streamable_http export REQUIRE_HTTPS=true export ALLOWED_ORIGINS=https://yourdomain.com -export SESSION_SECRET=your-secure-session-secret # Set provider-specific credentials ``` diff --git a/docs/vercel-deployment.md b/docs/vercel-deployment.md index da6253dc..854098fb 100644 --- a/docs/vercel-deployment.md +++ b/docs/vercel-deployment.md @@ -240,8 +240,6 @@ Generic OAuth provider support is planned but not yet implemented. Currently sup ```bash NODE_ENV=production -SESSION_SECRET=random-secret-at-least-32-chars -SESSION_TIMEOUT_MINUTES=60 REQUIRE_HTTPS=true ``` diff --git a/packages/config/src/base-config.ts b/packages/config/src/base-config.ts index efb343d7..5ac9eb14 100644 --- a/packages/config/src/base-config.ts +++ b/packages/config/src/base-config.ts @@ -40,12 +40,3 @@ export const BaseConfigSchema = z.object({ }); export type BaseConfig = z.infer; - -/** - * Session secret schema (separate for security) - */ -export const SessionSecretSchema = z.object({ - SESSION_SECRET: z.string().default('dev-session-secret-change-in-production'), -}); - -export type SessionSecret = z.infer; diff --git a/packages/config/src/environment.ts b/packages/config/src/environment.ts index cd691afb..ef8ce9c3 100644 --- a/packages/config/src/environment.ts +++ b/packages/config/src/environment.ts @@ -4,7 +4,7 @@ */ import { z } from 'zod'; -import { BaseConfigSchema, SessionSecretSchema, TransportMode } from './base-config.js'; +import { BaseConfigSchema, TransportMode } from './base-config.js'; import { OAuthConfigSchema, OAuthSecretsSchema } from './oauth-config.js'; import { LLMSecretsSchema } from './llm-config.js'; import { StorageConfigSchema } from './storage-config.js'; @@ -25,8 +25,7 @@ export const ConfigurationSchema = BaseConfigSchema /** * Secret configuration schema (never log) */ -export const SecretsSchema = SessionSecretSchema - .merge(OAuthSecretsSchema) +export const SecretsSchema = OAuthSecretsSchema .merge(LLMSecretsSchema); /** @@ -124,7 +123,6 @@ export class EnvironmentConfig { REQUIRE_HTTPS: process.env.REQUIRE_HTTPS === 'true', ALLOWED_ORIGINS: process.env.ALLOWED_ORIGINS, ALLOWED_HOSTS: process.env.ALLOWED_HOSTS, - SESSION_SECRET: process.env.SESSION_SECRET ?? 'dev-session-secret-change-in-production', NODE_ENV: process.env.NODE_ENV ?? 'development', // LLM Provider API keys @@ -169,20 +167,11 @@ export class EnvironmentConfig { for (const key of secretKeys) { const value = env[key]; - // Special handling for SESSION_SECRET which has a default value - if (key === 'SESSION_SECRET') { - if (value && value !== 'dev-session-secret-change-in-production') { - configured.push(key); - } else { - missing.push(key); - } + // Check if secret has a value + if (value) { + configured.push(key); } else { - // For all other secrets, just check if they have a value - if (value) { - configured.push(key); - } else { - missing.push(key); - } + missing.push(key); } } @@ -355,7 +344,6 @@ export class EnvironmentConfig { requireHttps: boolean; allowedOrigins: string[] | undefined; allowedHosts: string[] | undefined; - sessionSecret: string; } { const env = this.get(); @@ -363,7 +351,6 @@ export class EnvironmentConfig { requireHttps: env.REQUIRE_HTTPS || this.isProduction(), allowedOrigins: env.ALLOWED_ORIGINS ? env.ALLOWED_ORIGINS.split(',') : undefined, allowedHosts: env.ALLOWED_HOSTS ? env.ALLOWED_HOSTS.split(',') : undefined, - sessionSecret: env.SESSION_SECRET, }; } diff --git a/packages/config/test/environment.test.ts b/packages/config/test/environment.test.ts index dd603584..fda13022 100644 --- a/packages/config/test/environment.test.ts +++ b/packages/config/test/environment.test.ts @@ -25,7 +25,6 @@ describe('EnvironmentConfig', () => { delete process.env.REQUIRE_HTTPS; delete process.env.ALLOWED_ORIGINS; delete process.env.ALLOWED_HOSTS; - delete process.env.SESSION_SECRET; delete process.env.NODE_ENV; delete process.env.ANTHROPIC_API_KEY; delete process.env.OPENAI_API_KEY; @@ -47,7 +46,6 @@ describe('EnvironmentConfig', () => { expect(security.requireHttps).toBe(false); expect(security.allowedOrigins).toBeUndefined(); expect(security.allowedHosts).toBeUndefined(); - expect(security.sessionSecret).toBe('dev-session-secret-change-in-production'); }); test('parses provided environment variables into strongly typed configuration', () => { @@ -58,7 +56,6 @@ describe('EnvironmentConfig', () => { process.env.REQUIRE_HTTPS = 'true'; process.env.ALLOWED_ORIGINS = 'https://one.example,https://two.example'; process.env.ALLOWED_HOSTS = 'one.example,two.example'; - process.env.SESSION_SECRET = 'super-secret'; process.env.NODE_ENV = 'production'; EnvironmentConfig.reset(); @@ -86,7 +83,6 @@ describe('EnvironmentConfig', () => { 'one.example', 'two.example' ]); - expect(security.sessionSecret).toBe('super-secret'); }); describe('Secret Status Reporting', () => { @@ -124,21 +120,5 @@ describe('EnvironmentConfig', () => { expect(status.secrets.missing).toContain('GOOGLE_CLIENT_ID'); expect(status.secrets.missing).toContain('GOOGLE_CLIENT_SECRET'); }); - - it('correctly reports SESSION_SECRET as missing only when using default value', () => { - // Test with default value - should be missing - delete process.env.SESSION_SECRET; - EnvironmentConfig.reset(); - let status = EnvironmentConfig.getConfigurationStatus(); - expect(status.secrets.missing).toContain('SESSION_SECRET'); - expect(status.secrets.configured).not.toContain('SESSION_SECRET'); - - // Test with custom value - should be configured - process.env.SESSION_SECRET = 'my-custom-secret'; - EnvironmentConfig.reset(); - status = EnvironmentConfig.getConfigurationStatus(); - expect(status.secrets.configured).toContain('SESSION_SECRET'); - expect(status.secrets.missing).not.toContain('SESSION_SECRET'); - }); }); }); diff --git a/packages/create-mcp-typescript-simple/templates/env.example.hbs b/packages/create-mcp-typescript-simple/templates/env.example.hbs index 33b095ad..7dc27066 100644 --- a/packages/create-mcp-typescript-simple/templates/env.example.hbs +++ b/packages/create-mcp-typescript-simple/templates/env.example.hbs @@ -77,9 +77,6 @@ REQUIRE_HTTPS=false # Restrict allowed hosts (comma-separated) # ALLOWED_HOSTS=localhost,yourdomain.com -# Session secret (use a secure random string in production) -SESSION_SECRET=dev-session-secret-change-in-production - # ===== OAuth Dynamic Client Registration (DCR) Configuration ===== # Storage backend: auto (default), memory, file, redis # DCR_STORE_TYPE=auto diff --git a/packages/example-mcp/test/integration/admin-routes.test.ts b/packages/example-mcp/test/integration/admin-routes.test.ts index e215331f..badecf2e 100644 --- a/packages/example-mcp/test/integration/admin-routes.test.ts +++ b/packages/example-mcp/test/integration/admin-routes.test.ts @@ -58,7 +58,6 @@ describe('Admin Routes Integration', () => { host: 'localhost', endpoint: '/mcp', requireAuth: true, - sessionSecret: 'test-secret', enableResumability: true, enableJsonResponse: true, }); diff --git a/packages/example-mcp/test/integration/admin-token-endpoints.test.ts b/packages/example-mcp/test/integration/admin-token-endpoints.test.ts index d9ffdcb8..6afed66c 100644 --- a/packages/example-mcp/test/integration/admin-token-endpoints.test.ts +++ b/packages/example-mcp/test/integration/admin-token-endpoints.test.ts @@ -65,7 +65,6 @@ describe('Admin Token Management Endpoints Integration', () => { host: 'localhost', endpoint: '/mcp', requireAuth: true, - sessionSecret: 'test-secret', enableResumability: true, enableJsonResponse: true, }); diff --git a/packages/example-mcp/test/integration/dcr-endpoints.test.ts b/packages/example-mcp/test/integration/dcr-endpoints.test.ts index 51756e68..2339c90e 100644 --- a/packages/example-mcp/test/integration/dcr-endpoints.test.ts +++ b/packages/example-mcp/test/integration/dcr-endpoints.test.ts @@ -65,7 +65,6 @@ describe('OAuth 2.0 Dynamic Client Registration (DCR) Endpoints', () => { host: 'localhost', endpoint: '/mcp', requireAuth: true, - sessionSecret: 'test-secret-dcr', enableResumability: true, enableJsonResponse: true, }); diff --git a/packages/example-mcp/test/integration/discovery-endpoints.test.ts b/packages/example-mcp/test/integration/discovery-endpoints.test.ts index 01eeeccb..5b88fc17 100644 --- a/packages/example-mcp/test/integration/discovery-endpoints.test.ts +++ b/packages/example-mcp/test/integration/discovery-endpoints.test.ts @@ -58,7 +58,6 @@ describe('OAuth Discovery Endpoints Integration', () => { host: 'localhost', endpoint: '/mcp', requireAuth: true, - sessionSecret: 'test-secret', enableResumability: true, enableJsonResponse: true, }); @@ -105,7 +104,6 @@ describe('OAuth Discovery Endpoints Integration', () => { host: 'localhost', endpoint: '/mcp', requireAuth: false, - sessionSecret: 'test-secret', }); await serverNoAuth.initialize(); @@ -155,7 +153,6 @@ describe('OAuth Discovery Endpoints Integration', () => { host: 'localhost', endpoint: '/mcp', requireAuth: false, - sessionSecret: 'test-secret', }); await serverNoAuth.initialize(); @@ -206,7 +203,6 @@ describe('OAuth Discovery Endpoints Integration', () => { host: 'localhost', endpoint: '/mcp', requireAuth: false, - sessionSecret: 'test-secret', enableResumability: false, }); @@ -262,7 +258,6 @@ describe('OAuth Discovery Endpoints Integration', () => { host: 'localhost', endpoint: '/mcp', requireAuth: false, - sessionSecret: 'test-secret', }); await serverNoAuth.initialize(); diff --git a/packages/example-mcp/test/integration/health-routes.test.ts b/packages/example-mcp/test/integration/health-routes.test.ts index 8c03d784..9e0f8f78 100644 --- a/packages/example-mcp/test/integration/health-routes.test.ts +++ b/packages/example-mcp/test/integration/health-routes.test.ts @@ -55,7 +55,6 @@ describe('Health Routes Integration', () => { host: 'localhost', endpoint: '/mcp', requireAuth: true, - sessionSecret: 'test-secret', enableResumability: true, enableJsonResponse: true, }); @@ -298,7 +297,6 @@ describe('Health Routes Integration', () => { host: 'localhost', endpoint: '/mcp', requireAuth: false, - sessionSecret: 'test-secret', enableResumability: false, enableJsonResponse: false, }); diff --git a/packages/example-mcp/test/integration/openapi-compliance.test.ts b/packages/example-mcp/test/integration/openapi-compliance.test.ts index 0aa0e984..a6eb7ded 100644 --- a/packages/example-mcp/test/integration/openapi-compliance.test.ts +++ b/packages/example-mcp/test/integration/openapi-compliance.test.ts @@ -43,7 +43,6 @@ describe('OpenAPI Compliance Integration Tests', () => { host: 'localhost', endpoint: '/mcp', requireAuth: false, // No auth for compliance testing - sessionSecret: 'test-secret', }); await server.initialize(); diff --git a/packages/example-mcp/test/integration/route-coverage.test.ts b/packages/example-mcp/test/integration/route-coverage.test.ts index f509255f..d0f60df5 100644 --- a/packages/example-mcp/test/integration/route-coverage.test.ts +++ b/packages/example-mcp/test/integration/route-coverage.test.ts @@ -28,7 +28,6 @@ describe('Route Coverage - Detect Undocumented Routes', () => { host: 'localhost', endpoint: '/mcp', requireAuth: false, - sessionSecret: 'test-secret', }); await server.initialize(); diff --git a/packages/example-mcp/test/integration/session-reconstruction.test.ts b/packages/example-mcp/test/integration/session-reconstruction.test.ts index 2b66b560..22c79de9 100644 --- a/packages/example-mcp/test/integration/session-reconstruction.test.ts +++ b/packages/example-mcp/test/integration/session-reconstruction.test.ts @@ -60,7 +60,6 @@ describe('Session Reconstruction Integration Tests', () => { host: 'localhost', endpoint: '/mcp', requireAuth: false, // Skip OAuth for testing - sessionSecret: 'test-secret', enableResumability: true, // REQUIRED for session reconstruction tests enableJsonResponse: true, toolRegistry: toolRegistry, // Pass pre-populated tool registry for session reconstruction diff --git a/packages/http-server/src/server/streamable-http-server.ts b/packages/http-server/src/server/streamable-http-server.ts index c8a06ff9..1fa73f23 100644 --- a/packages/http-server/src/server/streamable-http-server.ts +++ b/packages/http-server/src/server/streamable-http-server.ts @@ -47,7 +47,6 @@ export interface StreamableHttpServerOptions { requireAuth: boolean; allowedOrigins?: string[]; allowedHosts?: string[]; - sessionSecret: string; enableResumability?: boolean; enableJsonResponse?: boolean; toolRegistry?: ToolRegistry; diff --git a/packages/http-server/src/transport/factory.ts b/packages/http-server/src/transport/factory.ts index 61487ed7..e17447e3 100644 --- a/packages/http-server/src/transport/factory.ts +++ b/packages/http-server/src/transport/factory.ts @@ -86,7 +86,6 @@ export class StreamableHTTPTransportManager implements TransportManager { requireAuth: this.options.requireAuth, allowedOrigins: this.options.allowedOrigins, allowedHosts: this.options.allowedHosts, - sessionSecret: this.options.sessionSecret, enableResumability: this.options.enableResumability, enableJsonResponse: this.options.enableJsonResponse, toolRegistry: this.toolRegistry, // Pass the tool registry for session reconstruction @@ -276,7 +275,6 @@ export class TransportFactory implements ITransportFactory { requireAuth: !EnvironmentConfig.shouldSkipAuth(), allowedOrigins: streamableSecurityConfig.allowedOrigins, allowedHosts: streamableSecurityConfig.allowedHosts, - sessionSecret: streamableSecurityConfig.sessionSecret, enableResumability: true, // Enable resumability by default enableJsonResponse: true, // Use JSON responses for HTTP clients }); diff --git a/packages/http-server/src/transport/types.ts b/packages/http-server/src/transport/types.ts index eb7734df..b89708bb 100644 --- a/packages/http-server/src/transport/types.ts +++ b/packages/http-server/src/transport/types.ts @@ -22,7 +22,6 @@ export interface StreamableHTTPTransportOptions { requireAuth: boolean; allowedOrigins?: string[]; allowedHosts?: string[]; - sessionSecret: string; enableResumability?: boolean; enableJsonResponse?: boolean; } diff --git a/packages/http-server/test/server/streamable-http-server.test.ts b/packages/http-server/test/server/streamable-http-server.test.ts index 8e98eb32..46cf838b 100644 --- a/packages/http-server/test/server/streamable-http-server.test.ts +++ b/packages/http-server/test/server/streamable-http-server.test.ts @@ -77,7 +77,6 @@ describe('MCPStreamableHttpServer', () => { host: '127.0.0.1', endpoint: '/stream', requireAuth: false, - sessionSecret: 'secret', enableResumability: false, enableJsonResponse: false, ...options diff --git a/packages/http-server/test/transport/factory.test.ts b/packages/http-server/test/transport/factory.test.ts index 5beac11f..f2ca1c92 100644 --- a/packages/http-server/test/transport/factory.test.ts +++ b/packages/http-server/test/transport/factory.test.ts @@ -30,7 +30,7 @@ describe('TransportFactory', () => { it('creates streamable HTTP transport with resumability enabled', () => { vi.spyOn(EnvironmentConfig, 'getTransportMode').mockReturnValue(TransportMode.STREAMABLE_HTTP); vi.spyOn(EnvironmentConfig, 'getServerConfig').mockReturnValue({ port: 3000, host: 'localhost', mode: TransportMode.STREAMABLE_HTTP }); - vi.spyOn(EnvironmentConfig, 'getSecurityConfig').mockReturnValue({ allowedOrigins: undefined, allowedHosts: undefined, sessionSecret: 'secret', requireHttps: false }); + vi.spyOn(EnvironmentConfig, 'getSecurityConfig').mockReturnValue({ allowedOrigins: undefined, allowedHosts: undefined, requireHttps: false }); vi.spyOn(EnvironmentConfig, 'shouldSkipAuth').mockReturnValue(true); const transport = TransportFactory.createFromEnvironment(); @@ -50,7 +50,6 @@ describe('TransportFactory', () => { requireAuth: false, allowedOrigins: [], allowedHosts: [], - sessionSecret: 'secret', enableResumability: false, enableJsonResponse: false }); @@ -92,7 +91,6 @@ describe('TransportFactory', () => { requireAuth: true, allowedOrigins: ['http://example.com'], allowedHosts: ['example.com'], - sessionSecret: 'test-secret', enableResumability: true, enableJsonResponse: true }; @@ -192,7 +190,6 @@ describe('TransportFactory', () => { requireAuth: false, allowedOrigins: ['*'], allowedHosts: ['localhost'], - sessionSecret: 'test-session-secret', enableResumability: true, enableJsonResponse: false }; @@ -224,7 +221,6 @@ describe('TransportFactory', () => { requireAuth: true, allowedOrigins: [], allowedHosts: [], - sessionSecret: 'secure-secret', enableResumability: true, enableJsonResponse: true });