1+ import express from 'express' ;
2+ import cors from 'cors' ;
3+ import { mcpAuthRouter } from '@modelcontextprotocol/sdk/server/auth/router.js' ;
4+ import { EverythingAuthProvider } from '../src/auth/provider.js' ;
5+ import { handleFakeAuthorize , handleFakeAuthorizeRedirect } from '../src/handlers/fakeauth.js' ;
6+ import { redisClient } from '../src/redis.js' ;
7+ import { logger } from '../src/utils/logger.js' ;
8+ import { AUTH_SERVER_PORT , AUTH_SERVER_URL } from '../src/config.js' ;
9+
10+ const app = express ( ) ;
11+
12+ console . log ( '=====================================' ) ;
13+ console . log ( 'MCP Demonstration Authorization Server' ) ;
14+ console . log ( '=====================================' ) ;
15+ console . log ( 'This standalone server demonstrates OAuth 2.0' ) ;
16+ console . log ( 'authorization separate from the MCP resource server' ) ;
17+ console . log ( '' ) ;
18+ console . log ( 'This is for demonstration purposes only.' ) ;
19+ console . log ( 'In production, you would use a real OAuth provider' ) ;
20+ console . log ( 'like Auth0, Okta, Google, GitHub, etc.' ) ;
21+ console . log ( '=====================================' ) ;
22+
23+ // CORS for Inspector and MCP server
24+ app . use ( cors ( {
25+ origin : true ,
26+ credentials : true
27+ } ) ) ;
28+
29+ app . use ( express . json ( ) ) ;
30+ app . use ( logger . middleware ( ) ) ;
31+
32+ // Health check endpoint
33+ app . get ( '/health' , ( req , res ) => {
34+ res . json ( {
35+ status : 'healthy' ,
36+ mode : 'authorization-server' ,
37+ endpoints : {
38+ metadata : `${ AUTH_SERVER_URL } /.well-known/oauth-authorization-server` ,
39+ authorize : `${ AUTH_SERVER_URL } /oauth/authorize` ,
40+ token : `${ AUTH_SERVER_URL } /oauth/token` ,
41+ register : `${ AUTH_SERVER_URL } /oauth/register` ,
42+ introspect : `${ AUTH_SERVER_URL } /oauth/introspect`
43+ }
44+ } ) ;
45+ } ) ;
46+
47+ // Create auth provider instance for reuse
48+ const authProvider = new EverythingAuthProvider ( ) ;
49+
50+ // OAuth endpoints via SDK's mcpAuthRouter
51+ app . use ( mcpAuthRouter ( {
52+ provider : authProvider ,
53+ issuerUrl : new URL ( AUTH_SERVER_URL ) ,
54+ tokenOptions : {
55+ rateLimit : { windowMs : 5000 , limit : 100 }
56+ } ,
57+ clientRegistrationOptions : {
58+ rateLimit : { windowMs : 60000 , limit : 10 }
59+ }
60+ } ) ) ;
61+
62+ // Token introspection endpoint (RFC 7662)
63+ app . post ( '/oauth/introspect' , express . urlencoded ( { extended : false } ) , async ( req , res ) => {
64+ try {
65+ const { token } = req . body ;
66+
67+ if ( ! token ) {
68+ return res . status ( 400 ) . json ( { error : 'invalid_request' , error_description : 'Missing token parameter' } ) ;
69+ }
70+
71+ // Verify the token using the auth provider
72+ const authInfo = await authProvider . verifyAccessToken ( token ) ;
73+
74+ // Return RFC 7662 compliant response
75+ res . json ( {
76+ active : true ,
77+ client_id : authInfo . clientId ,
78+ scope : authInfo . scopes . join ( ' ' ) ,
79+ exp : authInfo . expiresAt ,
80+ sub : authInfo . extra ?. userId || 'unknown' ,
81+ userId : authInfo . extra ?. userId , // Custom field for our implementation
82+ username : authInfo . extra ?. username ,
83+ iss : AUTH_SERVER_URL ,
84+ aud : authInfo . clientId ,
85+ token_type : 'Bearer'
86+ } ) ;
87+
88+ } catch ( error ) {
89+ logger . debug ( 'Token introspection failed' , { error : ( error as Error ) . message } ) ;
90+
91+ // Return inactive token response (don't leak error details)
92+ res . json ( {
93+ active : false
94+ } ) ;
95+ }
96+ } ) ;
97+
98+ // Fake upstream auth endpoints (for user authentication simulation)
99+ app . get ( '/fakeupstreamauth/authorize' , cors ( ) , handleFakeAuthorize ) ;
100+ app . get ( '/fakeupstreamauth/callback' , cors ( ) , handleFakeAuthorizeRedirect ) ;
101+
102+ // Static assets (for auth page styling)
103+ import path from 'path' ;
104+ import { fileURLToPath } from 'url' ;
105+
106+ const __filename = fileURLToPath ( import . meta. url ) ;
107+ const __dirname = path . dirname ( __filename ) ;
108+
109+ app . get ( '/mcp-logo.png' , ( req , res ) => {
110+ // Serve from the main server's static directory
111+ const logoPath = path . join ( __dirname , '../src/static/mcp.png' ) ;
112+ res . sendFile ( logoPath ) ;
113+ } ) ;
114+
115+ // Connect to Redis (shared with MCP server in dev)
116+ try {
117+ await redisClient . connect ( ) ;
118+ logger . info ( 'Connected to Redis' , { url : redisClient . options ?. url } ) ;
119+ } catch ( error ) {
120+ logger . error ( 'Could not connect to Redis' , error as Error ) ;
121+ process . exit ( 1 ) ;
122+ }
123+
124+ app . listen ( AUTH_SERVER_PORT , ( ) => {
125+ logger . info ( 'Authorization server started' , {
126+ port : AUTH_SERVER_PORT ,
127+ url : AUTH_SERVER_URL ,
128+ endpoints : {
129+ metadata : `${ AUTH_SERVER_URL } /.well-known/oauth-authorization-server` ,
130+ authorize : `${ AUTH_SERVER_URL } /oauth/authorize` ,
131+ token : `${ AUTH_SERVER_URL } /oauth/token` ,
132+ register : `${ AUTH_SERVER_URL } /oauth/register` ,
133+ introspect : `${ AUTH_SERVER_URL } /oauth/introspect`
134+ }
135+ } ) ;
136+
137+ console . log ( '' ) ;
138+ console . log ( '🚀 Auth server ready! Test with:' ) ;
139+ console . log ( ` curl ${ AUTH_SERVER_URL } /health` ) ;
140+ console . log ( ` curl ${ AUTH_SERVER_URL } /.well-known/oauth-authorization-server` ) ;
141+ console . log ( '' ) ;
142+ console . log ( '💡 To test separate mode:' ) ;
143+ console . log ( ' 1. Keep this server running' ) ;
144+ console . log ( ' 2. In another terminal: AUTH_MODE=separate npm run dev' ) ;
145+ console . log ( ' 3. Connect Inspector to http://localhost:3232' ) ;
146+ } ) ;
0 commit comments