Skip to content

Commit d608390

Browse files
committed
simplify router
1 parent f76bb96 commit d608390

File tree

3 files changed

+44
-93
lines changed

3 files changed

+44
-93
lines changed

src/examples/server/simpleStreamableHttp.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,9 +187,8 @@ if (useOAuth) {
187187
);
188188
// Add metadata routes to the main MCP server
189189
app.use(mcpAuthMetadataRouter({
190-
provider: remoteProvider,
190+
oauthMetadata,
191191
resourceServerUrl: mcpServerUrl,
192-
authorizationServerUrl: authServerUrl,
193192
scopesSupported: ['mcp:tools'],
194193
resourceName: 'MCP Demo Server',
195194
}));

src/server/auth/provider.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ export interface OAuthServerProvider {
2020
get clientsStore(): OAuthRegisteredClientsStore;
2121

2222
/**
23-
* Begins the authorization flow, which can either be implemented by this server itself or via redirection to a separate authorization server.
24-
*
23+
* Begins the authorization flow, which can either be implemented by this server itself or via redirection to a separate authorization server.
24+
*
2525
* This server must eventually issue a redirect with an authorization response or an error response to the given redirect URI. Per OAuth 2.1:
2626
* - In the successful case, the redirect MUST include the `code` and `state` (if present) query parameters.
2727
* - In the error case, the redirect MUST include the `error` query parameter, and MAY include an optional `error_description` query parameter.
@@ -50,17 +50,28 @@ export interface OAuthServerProvider {
5050

5151
/**
5252
* Revokes an access or refresh token. If unimplemented, token revocation is not supported (not recommended).
53-
*
53+
*
5454
* If the given token is invalid or already revoked, this method should do nothing.
5555
*/
5656
revokeToken?(client: OAuthClientInformationFull, request: OAuthTokenRevocationRequest): Promise<void>;
5757

5858
/**
5959
* Whether to skip local PKCE validation.
60-
*
60+
*
6161
* If true, the server will not perform PKCE validation locally and will pass the code_verifier to the upstream server.
62-
*
62+
*
6363
* NOTE: This should only be true if the upstream server is performing the actual PKCE validation.
6464
*/
6565
skipLocalPkceValidation?: boolean;
66-
}
66+
}
67+
68+
69+
/**
70+
* Slim implementation useful for token verification
71+
*/
72+
export interface TokenVerifier {
73+
/**
74+
* Verifies an access token and returns information about it.
75+
*/
76+
verifyAccessToken(token: string): Promise<AuthInfo>;
77+
}

src/server/auth/router.ts

Lines changed: 26 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,17 @@ export type AuthRouterOptions = {
3535
*/
3636
scopesSupported?: string[];
3737

38+
39+
/**
40+
* The resource name to be displayed in protected resource metadata
41+
*/
42+
resourceName?: string;
43+
3844
// Individual options per route
3945
authorizationOptions?: Omit<AuthorizationHandlerOptions, "provider">;
4046
clientRegistrationOptions?: Omit<ClientRegistrationHandlerOptions, "clientsStore">;
4147
revocationOptions?: Omit<RevocationHandlerOptions, "provider">;
4248
tokenOptions?: Omit<TokenHandlerOptions, "provider">;
43-
protectedResourceOptions?: Omit<ProtectedResourceRouterOptions, "issuerUrl" | "serviceDocumentationUrl" | "scopesSupported">;
4449
};
4550

4651
const checkIssuerUrl = (issuer: URL): void => {
@@ -109,49 +114,42 @@ export const createOAuthMetadata = (options: {
109114
* app.use(mcpAuthRouter(...));
110115
*/
111116
export function mcpAuthRouter(options: AuthRouterOptions): RequestHandler {
112-
const metadata = createOAuthMetadata(options);
117+
const oauthMetadata = createOAuthMetadata(options);
113118

114119
const router = express.Router();
115120

116121
router.use(
117-
new URL(metadata.authorization_endpoint).pathname,
122+
new URL(oauthMetadata.authorization_endpoint).pathname,
118123
authorizationHandler({ provider: options.provider, ...options.authorizationOptions })
119124
);
120125

121126
router.use(
122-
new URL(metadata.token_endpoint).pathname,
127+
new URL(oauthMetadata.token_endpoint).pathname,
123128
tokenHandler({ provider: options.provider, ...options.tokenOptions })
124129
);
125130

126-
router.use("/.well-known/oauth-authorization-server", metadataHandler(metadata));
127-
128-
// Always include protected resource metadata
129-
const defaultProtectedResourceOptions = {
130-
// Use issuer as the server URL if no override provided
131-
serverUrl: new URL(metadata.issuer),
132-
};
133-
134-
router.use(mcpProtectedResourceRouter({
135-
issuerUrl: options.issuerUrl,
131+
router.use(mcpAuthMetadataRouter({
132+
oauthMetadata,
133+
// This router is used for AS+RS combo's, so the issuer is also the resource server
134+
resourceServerUrl: new URL(oauthMetadata.issuer),
136135
serviceDocumentationUrl: options.serviceDocumentationUrl,
137136
scopesSupported: options.scopesSupported,
138-
...defaultProtectedResourceOptions,
139-
...options.protectedResourceOptions
140-
}))
137+
resourceName: options.resourceName
138+
}));
141139

142-
if (metadata.registration_endpoint) {
140+
if (oauthMetadata.registration_endpoint) {
143141
router.use(
144-
new URL(metadata.registration_endpoint).pathname,
142+
new URL(oauthMetadata.registration_endpoint).pathname,
145143
clientRegistrationHandler({
146144
clientsStore: options.provider.clientsStore,
147145
...options,
148146
})
149147
);
150148
}
151149

152-
if (metadata.revocation_endpoint) {
150+
if (oauthMetadata.revocation_endpoint) {
153151
router.use(
154-
new URL(metadata.revocation_endpoint).pathname,
152+
new URL(oauthMetadata.revocation_endpoint).pathname,
155153
revocationHandler({ provider: options.provider, ...options.revocationOptions })
156154
);
157155
}
@@ -164,83 +162,23 @@ export type AuthMetadataOptions = {
164162
* A provider implementing the actual authorization logic for this router.
165163
* Note: the provider should reference an authorization server
166164
*/
167-
provider: OAuthServerProvider;
165+
oauthMetadata: OAuthMetadata;
168166
resourceServerUrl: URL;
169-
authorizationServerUrl: URL;
170-
authorizationServerBaseUrl?: URL;
171167
serviceDocumentationUrl?: URL;
172168
scopesSupported?: string[];
173169
resourceName?: string;
174170
}
175171

176172
export function mcpAuthMetadataRouter(options: AuthMetadataOptions) {
177-
const router = express.Router();
178-
179-
const { provider,
180-
serviceDocumentationUrl,
181-
scopesSupported,
182-
resourceName
183-
} = options;
184-
185-
const metadata = createOAuthMetadata({
186-
provider,
187-
issuerUrl: options.authorizationServerUrl,
188-
baseUrl: options.authorizationServerBaseUrl,
189-
serviceDocumentationUrl,
190-
scopesSupported,
191-
});
192-
router.use("/.well-known/oauth-authorization-server", metadataHandler(metadata));
193-
194-
router.use(mcpProtectedResourceRouter({
195-
serverUrl: options.resourceServerUrl,
196-
issuerUrl: options.authorizationServerUrl,
197-
serviceDocumentationUrl,
198-
scopesSupported,
199-
resourceName,
200-
}))
201-
202-
return router;
203-
}
204-
205-
export type ProtectedResourceRouterOptions = {
206-
/**
207-
* The authorization server's issuer identifier, which is a URL that uses the "https" scheme and has no query or fragment components.
208-
*/
209-
issuerUrl: URL;
210-
211-
/**
212-
* The MCP server URL that is proteted.
213-
*
214-
*/
215-
serverUrl: URL;
216-
217-
/**
218-
* An optional URL of a page containing human-readable information that developers might want or need to know when using the authorization server.
219-
*/
220-
serviceDocumentationUrl?: URL;
221-
222-
/**
223-
* A list of valid scopes for the resource.
224-
*/
225-
scopesSupported?: Array<string>;
226-
227-
/**
228-
* A human readable resource name for the MCP server
229-
*/
230-
resourceName?: string;
231-
};
232-
233-
export function mcpProtectedResourceRouter(options: ProtectedResourceRouterOptions) {
234-
const issuer = options.issuerUrl;
235-
checkIssuerUrl(issuer);
173+
checkIssuerUrl(new URL(options.oauthMetadata.issuer));
236174

237175
const router = express.Router();
238176

239177
const protectedResourceMetadata: OAuthProtectedResourceMetadata = {
240-
resource: options.serverUrl.href,
178+
resource: options.resourceServerUrl.href,
241179

242180
authorization_servers: [
243-
issuer.href
181+
options.oauthMetadata.issuer
244182
],
245183

246184
scopes_supported: options.scopesSupported,
@@ -250,6 +188,9 @@ export function mcpProtectedResourceRouter(options: ProtectedResourceRouterOptio
250188

251189
router.use("/.well-known/oauth-protected-resource", metadataHandler(protectedResourceMetadata));
252190

191+
// Always add this for backwards compatibility
192+
router.use("/.well-known/oauth-authorization-server", metadataHandler(options.oauthMetadata));
193+
253194
return router;
254195
}
255196

0 commit comments

Comments
 (0)