Skip to content

Commit d282fa9

Browse files
committed
feat: Add API GW that serves up the .well-known/oauth-authorization-server path expected by MCP clients
1 parent 7881a12 commit d282fa9

File tree

7 files changed

+416
-41
lines changed

7 files changed

+416
-41
lines changed

DEVELOP.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,12 @@ aws iam attach-role-policy \
3535
cdk bootstrap aws://<aws account id>/us-east-2
3636
```
3737

38-
Deploy the Cognito user pool for OAuth authentication via the MCP streamable HTTP transport.
38+
The examples use Cognito for OAuth authentication used by MCP streamable HTTP transport.
39+
You will need your own domain name to use for auto-discovery of your Cognito endpoints
40+
by MCP clients. In `examples/servers/auth/lib/mcp-auth.ts`, replace `liguori.people.aws.dev`
41+
with your domain name. It must already be registered as a hosted zone in Route53.
42+
43+
Deploy the OAuth authentication stack.
3944

4045
```bash
4146
cd examples/servers/auth
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { APIGatewayProxyEvent, APIGatewayProxyResult } from "aws-lambda";
2+
3+
interface CognitoConfiguration {
4+
[key: string]: any;
5+
}
6+
7+
interface OAuthServerMetadata {
8+
[key: string]: any;
9+
code_challenge_methods_supported?: string[];
10+
}
11+
12+
export const handler = async (
13+
event: APIGatewayProxyEvent
14+
): Promise<APIGatewayProxyResult> => {
15+
const cognitoConfigUrl = process.env.COGNITO_OPENID_CONFIG_URL;
16+
17+
if (!cognitoConfigUrl) {
18+
console.error("COGNITO_OPENID_CONFIG_URL environment variable not set");
19+
return {
20+
statusCode: 500,
21+
headers: {
22+
"Content-Type": "application/json",
23+
"Access-Control-Allow-Origin": "*",
24+
},
25+
body: JSON.stringify({ error: "Configuration error" }),
26+
};
27+
}
28+
29+
try {
30+
// Fetch Cognito's OpenID configuration
31+
const cognitoConfig = await fetchJson(cognitoConfigUrl);
32+
33+
// Add the missing code_challenge_methods_supported field
34+
const modifiedConfig: OAuthServerMetadata = {
35+
...cognitoConfig,
36+
code_challenge_methods_supported: ["S256"],
37+
};
38+
39+
return {
40+
statusCode: 200,
41+
headers: {
42+
"Content-Type": "application/json",
43+
"Access-Control-Allow-Origin": "*",
44+
"Cache-Control": "public, max-age=3600", // Cache for 1 hour
45+
},
46+
body: JSON.stringify(modifiedConfig, null, 2),
47+
};
48+
} catch (error) {
49+
console.error("Error fetching Cognito config:", error);
50+
return {
51+
statusCode: 500,
52+
headers: {
53+
"Content-Type": "application/json",
54+
"Access-Control-Allow-Origin": "*",
55+
},
56+
body: JSON.stringify({ error: "Failed to fetch OAuth configuration" }),
57+
};
58+
}
59+
};
60+
61+
async function fetchJson(url: string): Promise<CognitoConfiguration> {
62+
const response = await fetch(url, {
63+
method: "GET",
64+
headers: {
65+
"User-Agent": "MCP-OAuth-Proxy/1.0",
66+
},
67+
// Add timeout using AbortController
68+
signal: AbortSignal.timeout(5000),
69+
});
70+
71+
if (!response.ok) {
72+
throw new Error(`HTTP error! status: ${response.status}`);
73+
}
74+
75+
return await response.json();
76+
}

0 commit comments

Comments
 (0)