@@ -38,6 +38,30 @@ export interface SetupAuthServerOptions {
3838let globalAuth : DemoAuth | null = null ;
3939let demoUserCreated = false ;
4040
41+ const DEFAULT_CORS_ORIGIN_REGEX = / ^ h t t p s ? : \/ \/ (?: l o c a l h o s t | 1 2 7 \. 0 \. 0 \. 1 | \[ : : 1 \] ) (?: : \d + ) ? $ / ;
42+
43+ function buildCorsMiddleware ( ) : ReturnType < typeof cors > {
44+ let corsOriginRegex = DEFAULT_CORS_ORIGIN_REGEX ;
45+ if ( process . env . MCP_CORS_ORIGIN_REGEX ) {
46+ try {
47+ corsOriginRegex = new RegExp ( process . env . MCP_CORS_ORIGIN_REGEX ) ;
48+ } catch ( error ) {
49+ const msg =
50+ error && typeof error === 'object' && 'message' in error ? String ( ( error as { message : unknown } ) . message ) : String ( error ) ;
51+ console . warn ( `Invalid MCP_CORS_ORIGIN_REGEX (${ process . env . MCP_CORS_ORIGIN_REGEX } ): ${ msg } ` ) ;
52+ corsOriginRegex = DEFAULT_CORS_ORIGIN_REGEX ;
53+ }
54+ }
55+
56+ return cors ( {
57+ origin : ( origin , cb ) => {
58+ // Allow non-browser clients (no Origin header).
59+ if ( ! origin ) return cb ( null , true ) ;
60+ return cb ( null , corsOriginRegex . test ( origin ) ) ;
61+ }
62+ } ) ;
63+ }
64+
4165/**
4266 * Gets the global auth instance (must call setupAuthServer first)
4367 */
@@ -102,13 +126,11 @@ export function setupAuthServer(options: SetupAuthServerOptions): void {
102126 // Create Express app for auth server
103127 const authApp = express ( ) ;
104128
105- // Enable CORS for all origins (demo only) - must be before other middleware
106- // WARNING: This configuration is for demo purposes only. In production, you should restrict this to specific origins and configure CORS yourself.
107- authApp . use (
108- cors ( {
109- origin : '*' // WARNING: This allows all origins to access the auth server. In production, you should restrict this to specific origins.
110- } )
111- ) ;
129+ // CORS: allow only loopback origins by default (typical for local dev / Inspector direct connect).
130+ // If you intentionally expose this demo remotely, set MCP_CORS_ORIGIN_REGEX explicitly.
131+ // Must be before other middleware.
132+ const corsMw = buildCorsMiddleware ( ) ;
133+ authApp . use ( corsMw ) ;
112134
113135 // Create better-auth handler
114136 // toNodeHandler bypasses Express methods
@@ -163,8 +185,8 @@ export function setupAuthServer(options: SetupAuthServerOptions): void {
163185
164186 // OAuth metadata endpoints using better-auth's built-in handlers
165187 // Add explicit OPTIONS handler for CORS preflight
166- authApp . options ( '/.well-known/oauth-authorization-server' , cors ( ) ) ;
167- authApp . get ( '/.well-known/oauth-authorization-server' , cors ( ) , toNodeHandler ( oAuthDiscoveryMetadata ( auth ) ) ) ;
188+ authApp . options ( '/.well-known/oauth-authorization-server' , corsMw ) ;
189+ authApp . get ( '/.well-known/oauth-authorization-server' , corsMw , toNodeHandler ( oAuthDiscoveryMetadata ( auth ) ) ) ;
168190
169191 // Body parsers for non-better-auth routes (like /sign-in)
170192 const maxBodyBytes = 100 * 1024 ; // Make the default explicit to avoid accidental large-body DoS.
@@ -273,14 +295,15 @@ export function setupAuthServer(options: SetupAuthServerOptions): void {
273295export function createProtectedResourceMetadataRouter ( resourcePath = '/mcp' ) : Router {
274296 const auth = getAuth ( ) ;
275297 const router = express . Router ( ) ;
298+ const corsMw = buildCorsMiddleware ( ) ;
276299
277300 // Construct the metadata path per RFC 9728 Section 3
278301 const metadataPath = `/.well-known/oauth-protected-resource${ resourcePath } ` ;
279302
280303 // Enable CORS for browser-based clients to discover the auth server
281304 // Add explicit OPTIONS handler for CORS preflight
282- router . options ( metadataPath , cors ( ) ) ;
283- router . get ( metadataPath , cors ( ) , toNodeHandler ( oAuthProtectedResourceMetadata ( auth ) ) ) ;
305+ router . options ( metadataPath , corsMw ) ;
306+ router . get ( metadataPath , corsMw , toNodeHandler ( oAuthProtectedResourceMetadata ( auth ) ) ) ;
284307
285308 return router ;
286309}
0 commit comments