7
7
NOTE: this is a simplified example for demonstration purposes.
8
8
This is not a production-ready implementation.
9
9
10
- Usage:
11
- python -m mcp_simple_auth.auth_server --port=9000
12
10
"""
13
11
14
12
import asyncio
20
18
from starlette .applications import Starlette
21
19
from starlette .exceptions import HTTPException
22
20
from starlette .requests import Request
23
- from starlette .responses import JSONResponse , RedirectResponse , Response
21
+ from starlette .responses import JSONResponse , Response
24
22
from starlette .routing import Route
25
23
from uvicorn import Config , Server
26
24
27
25
from mcp .server .auth .routes import cors_middleware , create_auth_routes
28
26
from mcp .server .auth .settings import AuthSettings , ClientRegistrationOptions
29
27
30
- from .github_oauth_provider import GitHubOAuthProvider , GitHubOAuthSettings
28
+ from .simple_auth_provider import SimpleAuthSettings , SimpleOAuthProvider
31
29
32
30
logger = logging .getLogger (__name__ )
33
31
@@ -39,60 +37,64 @@ class AuthServerSettings(BaseModel):
39
37
host : str = "localhost"
40
38
port : int = 9000
41
39
server_url : AnyHttpUrl = AnyHttpUrl ("http://localhost:9000" )
42
- github_callback_path : str = "http://localhost:9000/github /callback"
40
+ auth_callback_path : str = "http://localhost:9000/login /callback"
43
41
44
42
45
- class GitHubProxyAuthProvider ( GitHubOAuthProvider ):
43
+ class SimpleAuthProvider ( SimpleOAuthProvider ):
46
44
"""
47
- Authorization Server provider that proxies GitHub OAuth .
45
+ Authorization Server provider with simple demo authentication .
48
46
49
47
This provider:
50
- 1. Issues MCP tokens after GitHub authentication
48
+ 1. Issues MCP tokens after simple credential authentication
51
49
2. Stores token state for introspection by Resource Servers
52
- 3. Maps MCP tokens to GitHub tokens for API access
53
50
"""
54
51
55
- def __init__ (self , github_settings : GitHubOAuthSettings , github_callback_path : str ):
56
- super ().__init__ (github_settings , github_callback_path )
52
+ def __init__ (self , auth_settings : SimpleAuthSettings , auth_callback_path : str , server_url : str ):
53
+ super ().__init__ (auth_settings , auth_callback_path , server_url )
57
54
58
55
59
- def create_authorization_server (server_settings : AuthServerSettings , github_settings : GitHubOAuthSettings ) -> Starlette :
56
+ def create_authorization_server (server_settings : AuthServerSettings , auth_settings : SimpleAuthSettings ) -> Starlette :
60
57
"""Create the Authorization Server application."""
61
- oauth_provider = GitHubProxyAuthProvider (github_settings , server_settings .github_callback_path )
58
+ oauth_provider = SimpleAuthProvider (
59
+ auth_settings , server_settings .auth_callback_path , str (server_settings .server_url )
60
+ )
62
61
63
- auth_settings = AuthSettings (
62
+ mcp_auth_settings = AuthSettings (
64
63
issuer_url = server_settings .server_url ,
65
64
client_registration_options = ClientRegistrationOptions (
66
65
enabled = True ,
67
- valid_scopes = [github_settings .mcp_scope ],
68
- default_scopes = [github_settings .mcp_scope ],
66
+ valid_scopes = [auth_settings .mcp_scope ],
67
+ default_scopes = [auth_settings .mcp_scope ],
69
68
),
70
- required_scopes = [github_settings .mcp_scope ],
69
+ required_scopes = [auth_settings .mcp_scope ],
71
70
resource_server_url = None ,
72
71
)
73
72
74
73
# Create OAuth routes
75
74
routes = create_auth_routes (
76
75
provider = oauth_provider ,
77
- issuer_url = auth_settings .issuer_url ,
78
- service_documentation_url = auth_settings .service_documentation_url ,
79
- client_registration_options = auth_settings .client_registration_options ,
80
- revocation_options = auth_settings .revocation_options ,
76
+ issuer_url = mcp_auth_settings .issuer_url ,
77
+ service_documentation_url = mcp_auth_settings .service_documentation_url ,
78
+ client_registration_options = mcp_auth_settings .client_registration_options ,
79
+ revocation_options = mcp_auth_settings .revocation_options ,
81
80
)
82
81
83
- # Add GitHub callback route
84
- async def github_callback_handler (request : Request ) -> Response :
85
- """Handle GitHub OAuth callback."""
86
- code = request .query_params .get ("code" )
82
+ # Add login page route (GET)
83
+ async def login_page_handler (request : Request ) -> Response :
84
+ """Show login form."""
87
85
state = request .query_params .get ("state" )
86
+ if not state :
87
+ raise HTTPException (400 , "Missing state parameter" )
88
+ return await oauth_provider .get_login_page (state )
88
89
89
- if not code or not state :
90
- raise HTTPException (400 , "Missing code or state parameter" )
90
+ routes .append (Route ("/login" , endpoint = login_page_handler , methods = ["GET" ]))
91
91
92
- redirect_uri = await oauth_provider .handle_github_callback (code , state )
93
- return RedirectResponse (url = redirect_uri , status_code = 302 )
92
+ # Add login callback route (POST)
93
+ async def login_callback_handler (request : Request ) -> Response :
94
+ """Handle simple authentication callback."""
95
+ return await oauth_provider .handle_login_callback (request )
94
96
95
- routes .append (Route ("/github /callback" , endpoint = github_callback_handler , methods = ["GET " ]))
97
+ routes .append (Route ("/login /callback" , endpoint = login_callback_handler , methods = ["POST " ]))
96
98
97
99
# Add token introspection endpoint (RFC 7662) for Resource Servers
98
100
async def introspect_handler (request : Request ) -> Response :
@@ -112,7 +114,6 @@ async def introspect_handler(request: Request) -> Response:
112
114
if not access_token :
113
115
return JSONResponse ({"active" : False })
114
116
115
- # Return token info for Resource Server
116
117
return JSONResponse (
117
118
{
118
119
"active" : True ,
@@ -133,39 +134,12 @@ async def introspect_handler(request: Request) -> Response:
133
134
)
134
135
)
135
136
136
- # Add GitHub user info endpoint (for Resource Server to fetch user data)
137
- async def github_user_handler (request : Request ) -> Response :
138
- """
139
- Proxy endpoint to get GitHub user info using stored GitHub tokens.
140
-
141
- Resource Servers call this with MCP tokens to get GitHub user data
142
- without exposing GitHub tokens to clients.
143
- """
144
- # Extract Bearer token
145
- auth_header = request .headers .get ("authorization" , "" )
146
- if not auth_header .startswith ("Bearer " ):
147
- return JSONResponse ({"error" : "unauthorized" }, status_code = 401 )
148
-
149
- mcp_token = auth_header [7 :]
150
-
151
- # Get GitHub user info using the provider method
152
- user_info = await oauth_provider .get_github_user_info (mcp_token )
153
- return JSONResponse (user_info )
154
-
155
- routes .append (
156
- Route (
157
- "/github/user" ,
158
- endpoint = cors_middleware (github_user_handler , ["GET" , "OPTIONS" ]),
159
- methods = ["GET" , "OPTIONS" ],
160
- )
161
- )
162
-
163
137
return Starlette (routes = routes )
164
138
165
139
166
- async def run_server (server_settings : AuthServerSettings , github_settings : GitHubOAuthSettings ):
140
+ async def run_server (server_settings : AuthServerSettings , auth_settings : SimpleAuthSettings ):
167
141
"""Run the Authorization Server."""
168
- auth_server = create_authorization_server (server_settings , github_settings )
142
+ auth_server = create_authorization_server (server_settings , auth_settings )
169
143
170
144
config = Config (
171
145
auth_server ,
@@ -175,22 +149,7 @@ async def run_server(server_settings: AuthServerSettings, github_settings: GitHu
175
149
)
176
150
server = Server (config )
177
151
178
- logger .info ("=" * 80 )
179
- logger .info ("MCP AUTHORIZATION SERVER" )
180
- logger .info ("=" * 80 )
181
- logger .info (f"Server URL: { server_settings .server_url } " )
182
- logger .info ("Endpoints:" )
183
- logger .info (f" - OAuth Metadata: { server_settings .server_url } /.well-known/oauth-authorization-server" )
184
- logger .info (f" - Client Registration: { server_settings .server_url } /register" )
185
- logger .info (f" - Authorization: { server_settings .server_url } /authorize" )
186
- logger .info (f" - Token Exchange: { server_settings .server_url } /token" )
187
- logger .info (f" - Token Introspection: { server_settings .server_url } /introspect" )
188
- logger .info (f" - GitHub Callback: { server_settings .server_url } /github/callback" )
189
- logger .info (f" - GitHub User Proxy: { server_settings .server_url } /github/user" )
190
- logger .info ("" )
191
- logger .info ("Resource Servers should use /introspect to validate tokens" )
192
- logger .info ("Configure GitHub App callback URL: " + server_settings .github_callback_path )
193
- logger .info ("=" * 80 )
152
+ logger .info (f"🚀 MCP Authorization Server running on { server_settings .server_url } " )
194
153
195
154
await server .serve ()
196
155
@@ -203,18 +162,12 @@ def main(port: int) -> int:
203
162
204
163
This server handles OAuth flows and can be used by multiple Resource Servers.
205
164
206
- Environment variables needed:
207
- - MCP_GITHUB_CLIENT_ID: GitHub OAuth Client ID
208
- - MCP_GITHUB_CLIENT_SECRET: GitHub OAuth Client Secret
165
+ Uses simple hardcoded credentials for demo purposes.
209
166
"""
210
167
logging .basicConfig (level = logging .INFO )
211
168
212
- # Load GitHub settings from environment variables
213
- github_settings = GitHubOAuthSettings ()
214
-
215
- # Validate required fields
216
- if not github_settings .github_client_id or not github_settings .github_client_secret :
217
- raise ValueError ("GitHub credentials not provided" )
169
+ # Load simple auth settings
170
+ auth_settings = SimpleAuthSettings ()
218
171
219
172
# Create server settings
220
173
host = "localhost"
@@ -223,10 +176,10 @@ def main(port: int) -> int:
223
176
host = host ,
224
177
port = port ,
225
178
server_url = AnyHttpUrl (server_url ),
226
- github_callback_path = f"{ server_url } /github/callback " ,
179
+ auth_callback_path = f"{ server_url } /login " ,
227
180
)
228
181
229
- asyncio .run (run_server (server_settings , github_settings ))
182
+ asyncio .run (run_server (server_settings , auth_settings ))
230
183
return 0
231
184
232
185
0 commit comments