Skip to content

Commit 80b5e37

Browse files
tbrandtbrandaws
andauthored
Fix for verifyToken failure bug in closed network mode (#1252)
Co-authored-by: Taichiro Suzuki <taichirs@amazon.co.jp>
1 parent 3880274 commit 80b5e37

File tree

5 files changed

+283
-9
lines changed

5 files changed

+283
-9
lines changed

packages/cdk/lambda/utils/auth.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,29 @@
1-
import { CognitoJwtVerifier } from 'aws-jwt-verify';
1+
import { JwtRsaVerifier } from 'aws-jwt-verify';
22
import { CognitoIdTokenPayload } from 'aws-jwt-verify/jwt-model';
33

44
export const verifyToken = async (
55
token: string
66
): Promise<CognitoIdTokenPayload | undefined> => {
77
try {
8-
const verifier = CognitoJwtVerifier.create({
9-
userPoolId: process.env.USER_POOL_ID || '',
8+
const region = process.env.AWS_REGION!;
9+
const userPoolId = process.env.USER_POOL_ID!;
10+
const clientId = process.env.USER_POOL_CLIENT_ID!;
11+
const proxyEndpoint = process.env.USER_POOL_PROXY_ENDPOINT!;
12+
13+
const jwksUri =
14+
proxyEndpoint.length > 0
15+
? `${proxyEndpoint}${userPoolId}/.well-known/jwks.json`
16+
: `https://cognito-idp.${region}.amazonaws.com/${userPoolId}/.well-known/jwks.json`;
17+
18+
const verifier = JwtRsaVerifier.create({
19+
issuer: `https://cognito-idp.${region}.amazonaws.com/${userPoolId}`,
20+
audience: clientId,
21+
jwksUri,
1022
tokenUse: 'id',
11-
clientId: process.env.USER_POOL_CLIENT_ID || '',
1223
});
24+
1325
const payload = await verifier.verify(token);
14-
return payload;
26+
return payload as CognitoIdTokenPayload;
1527
} catch {
1628
return undefined;
1729
}

packages/cdk/lib/construct/api.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ export interface BackendApiProps {
7272
readonly vpc?: IVpc;
7373
readonly securityGroups?: ISecurityGroup[];
7474
readonly apiGatewayVpcEndpoint?: InterfaceVpcEndpoint;
75+
readonly cognitoUserPoolProxyEndpoint?: string;
7576
}
7677

7778
export class Api extends Construct {
@@ -205,6 +206,7 @@ export class Api extends Construct {
205206
environment: {
206207
USER_POOL_ID: userPool.userPoolId,
207208
USER_POOL_CLIENT_ID: userPoolClient.userPoolClientId,
209+
USER_POOL_PROXY_ENDPOINT: props.cognitoUserPoolProxyEndpoint ?? '',
208210
MODEL_REGION: modelRegion,
209211
MODEL_IDS: JSON.stringify(modelIds),
210212
IMAGE_GENERATION_MODEL_IDS: JSON.stringify(imageGenerationModelIds),

packages/cdk/lib/construct/closedNetwork/cognito-private-proxy.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export class CognitoPrivateProxy extends Construct {
2626
restApiName: 'GenU Cognito UserPool Proxy API',
2727
defaultCorsPreflightOptions: {
2828
allowOrigins: agw.Cors.ALL_ORIGINS,
29-
allowMethods: ['POST', 'OPTIONS'],
29+
allowMethods: ['GET', 'POST', 'OPTIONS'],
3030
allowHeaders: [
3131
'amz-sdk-invocation-id',
3232
'amz-sdk-request',
@@ -64,6 +64,44 @@ export class CognitoPrivateProxy extends Construct {
6464
}
6565
);
6666

67+
const userPoolResource =
68+
this.cognitoUserPoolProxyApi.root.addResource('{userPoolId}');
69+
const wellKnownResource = userPoolResource.addResource('.well-known');
70+
const jwksResource = wellKnownResource.addResource('jwks.json');
71+
72+
// Add GET method to the root resource to serve jwtks.json
73+
jwksResource.addMethod(
74+
'GET',
75+
new agw.HttpIntegration(
76+
`${cognitoUserPoolEndpoint}/{userPoolId}/.well-known/jwks.json`,
77+
{
78+
proxy: false,
79+
httpMethod: 'GET',
80+
options: {
81+
requestParameters: {
82+
'integration.request.path.userPoolId':
83+
'method.request.path.userPoolId',
84+
},
85+
integrationResponses: [
86+
{
87+
statusCode: '200',
88+
},
89+
],
90+
},
91+
}
92+
),
93+
{
94+
requestParameters: {
95+
'method.request.path.userPoolId': true,
96+
},
97+
methodResponses: [
98+
{
99+
statusCode: '200',
100+
},
101+
],
102+
}
103+
);
104+
67105
// Add POST method to the root resource to proxy Cognito UserPool requests
68106
this.cognitoUserPoolProxyApi.root.addMethod(
69107
'POST',

packages/cdk/lib/generative-ai-use-cases-stack.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ export class GenerativeAiUseCasesStack extends Stack {
124124
vpc: props.vpc,
125125
securityGroups,
126126
apiGatewayVpcEndpoint: props.apiGatewayVpcEndpoint,
127+
cognitoUserPoolProxyEndpoint: props.cognitoUserPoolProxyEndpoint,
127128
});
128129

129130
// WAF

packages/cdk/test/__snapshots__/generative-ai-use-cases.test.ts.snap

Lines changed: 224 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1808,8 +1808,15 @@ exports[`GenerativeAiUseCases matches the snapshot (closed network mode) 1`] = `
18081808
"Type": "AWS::IAM::Role",
18091809
"UpdateReplacePolicy": "Retain",
18101810
},
1811-
"CognitoPrivateProxyCognitoUserPoolProxyApiDeployment11D70EC105771a0029216e27aa20a3859719b309": {
1811+
"CognitoPrivateProxyCognitoUserPoolProxyApiDeployment11D70EC1b5bb02dcd4261d6e44e76aa0863f6287": {
18121812
"DependsOn": [
1813+
"CognitoPrivateProxyCognitoUserPoolProxyApiuserPoolIdwellknownjwksjsonGET21D71187",
1814+
"CognitoPrivateProxyCognitoUserPoolProxyApiuserPoolIdwellknownjwksjsonOPTIONSA2D13B9E",
1815+
"CognitoPrivateProxyCognitoUserPoolProxyApiuserPoolIdwellknownjwksjsonD9640238",
1816+
"CognitoPrivateProxyCognitoUserPoolProxyApiuserPoolIdwellknownOPTIONSB9A49912",
1817+
"CognitoPrivateProxyCognitoUserPoolProxyApiuserPoolIdwellknownCE432EBE",
1818+
"CognitoPrivateProxyCognitoUserPoolProxyApiuserPoolIdOPTIONSC878F5C3",
1819+
"CognitoPrivateProxyCognitoUserPoolProxyApiuserPoolId5D61C608",
18131820
"CognitoPrivateProxyCognitoUserPoolProxyApiOPTIONSCE339C8F",
18141821
"CognitoPrivateProxyCognitoUserPoolProxyApiPOST0CAE9A7C",
18151822
],
@@ -1830,7 +1837,7 @@ exports[`GenerativeAiUseCases matches the snapshot (closed network mode) 1`] = `
18301837
],
18311838
"Properties": {
18321839
"DeploymentId": {
1833-
"Ref": "CognitoPrivateProxyCognitoUserPoolProxyApiDeployment11D70EC105771a0029216e27aa20a3859719b309",
1840+
"Ref": "CognitoPrivateProxyCognitoUserPoolProxyApiDeployment11D70EC1b5bb02dcd4261d6e44e76aa0863f6287",
18341841
},
18351842
"RestApiId": {
18361843
"Ref": "CognitoPrivateProxyCognitoUserPoolProxyApi818B53B1",
@@ -1849,7 +1856,7 @@ exports[`GenerativeAiUseCases matches the snapshot (closed network mode) 1`] = `
18491856
{
18501857
"ResponseParameters": {
18511858
"method.response.header.Access-Control-Allow-Headers": "'amz-sdk-invocation-id,amz-sdk-request,cache-control,content-type,x-amz-target,x-amz-user-agent'",
1852-
"method.response.header.Access-Control-Allow-Methods": "'POST,OPTIONS'",
1859+
"method.response.header.Access-Control-Allow-Methods": "'GET,POST,OPTIONS'",
18531860
"method.response.header.Access-Control-Allow-Origin": "'*'",
18541861
},
18551862
"StatusCode": "204",
@@ -1936,6 +1943,199 @@ exports[`GenerativeAiUseCases matches the snapshot (closed network mode) 1`] = `
19361943
},
19371944
"Type": "AWS::ApiGateway::Method",
19381945
},
1946+
"CognitoPrivateProxyCognitoUserPoolProxyApiuserPoolId5D61C608": {
1947+
"Properties": {
1948+
"ParentId": {
1949+
"Fn::GetAtt": [
1950+
"CognitoPrivateProxyCognitoUserPoolProxyApi818B53B1",
1951+
"RootResourceId",
1952+
],
1953+
},
1954+
"PathPart": "{userPoolId}",
1955+
"RestApiId": {
1956+
"Ref": "CognitoPrivateProxyCognitoUserPoolProxyApi818B53B1",
1957+
},
1958+
},
1959+
"Type": "AWS::ApiGateway::Resource",
1960+
},
1961+
"CognitoPrivateProxyCognitoUserPoolProxyApiuserPoolIdOPTIONSC878F5C3": {
1962+
"Properties": {
1963+
"ApiKeyRequired": false,
1964+
"AuthorizationType": "NONE",
1965+
"HttpMethod": "OPTIONS",
1966+
"Integration": {
1967+
"IntegrationResponses": [
1968+
{
1969+
"ResponseParameters": {
1970+
"method.response.header.Access-Control-Allow-Headers": "'amz-sdk-invocation-id,amz-sdk-request,cache-control,content-type,x-amz-target,x-amz-user-agent'",
1971+
"method.response.header.Access-Control-Allow-Methods": "'GET,POST,OPTIONS'",
1972+
"method.response.header.Access-Control-Allow-Origin": "'*'",
1973+
},
1974+
"StatusCode": "204",
1975+
},
1976+
],
1977+
"RequestTemplates": {
1978+
"application/json": "{ statusCode: 200 }",
1979+
},
1980+
"Type": "MOCK",
1981+
},
1982+
"MethodResponses": [
1983+
{
1984+
"ResponseParameters": {
1985+
"method.response.header.Access-Control-Allow-Headers": true,
1986+
"method.response.header.Access-Control-Allow-Methods": true,
1987+
"method.response.header.Access-Control-Allow-Origin": true,
1988+
},
1989+
"StatusCode": "204",
1990+
},
1991+
],
1992+
"ResourceId": {
1993+
"Ref": "CognitoPrivateProxyCognitoUserPoolProxyApiuserPoolId5D61C608",
1994+
},
1995+
"RestApiId": {
1996+
"Ref": "CognitoPrivateProxyCognitoUserPoolProxyApi818B53B1",
1997+
},
1998+
},
1999+
"Type": "AWS::ApiGateway::Method",
2000+
},
2001+
"CognitoPrivateProxyCognitoUserPoolProxyApiuserPoolIdwellknownCE432EBE": {
2002+
"Properties": {
2003+
"ParentId": {
2004+
"Ref": "CognitoPrivateProxyCognitoUserPoolProxyApiuserPoolId5D61C608",
2005+
},
2006+
"PathPart": ".well-known",
2007+
"RestApiId": {
2008+
"Ref": "CognitoPrivateProxyCognitoUserPoolProxyApi818B53B1",
2009+
},
2010+
},
2011+
"Type": "AWS::ApiGateway::Resource",
2012+
},
2013+
"CognitoPrivateProxyCognitoUserPoolProxyApiuserPoolIdwellknownOPTIONSB9A49912": {
2014+
"Properties": {
2015+
"ApiKeyRequired": false,
2016+
"AuthorizationType": "NONE",
2017+
"HttpMethod": "OPTIONS",
2018+
"Integration": {
2019+
"IntegrationResponses": [
2020+
{
2021+
"ResponseParameters": {
2022+
"method.response.header.Access-Control-Allow-Headers": "'amz-sdk-invocation-id,amz-sdk-request,cache-control,content-type,x-amz-target,x-amz-user-agent'",
2023+
"method.response.header.Access-Control-Allow-Methods": "'GET,POST,OPTIONS'",
2024+
"method.response.header.Access-Control-Allow-Origin": "'*'",
2025+
},
2026+
"StatusCode": "204",
2027+
},
2028+
],
2029+
"RequestTemplates": {
2030+
"application/json": "{ statusCode: 200 }",
2031+
},
2032+
"Type": "MOCK",
2033+
},
2034+
"MethodResponses": [
2035+
{
2036+
"ResponseParameters": {
2037+
"method.response.header.Access-Control-Allow-Headers": true,
2038+
"method.response.header.Access-Control-Allow-Methods": true,
2039+
"method.response.header.Access-Control-Allow-Origin": true,
2040+
},
2041+
"StatusCode": "204",
2042+
},
2043+
],
2044+
"ResourceId": {
2045+
"Ref": "CognitoPrivateProxyCognitoUserPoolProxyApiuserPoolIdwellknownCE432EBE",
2046+
},
2047+
"RestApiId": {
2048+
"Ref": "CognitoPrivateProxyCognitoUserPoolProxyApi818B53B1",
2049+
},
2050+
},
2051+
"Type": "AWS::ApiGateway::Method",
2052+
},
2053+
"CognitoPrivateProxyCognitoUserPoolProxyApiuserPoolIdwellknownjwksjsonD9640238": {
2054+
"Properties": {
2055+
"ParentId": {
2056+
"Ref": "CognitoPrivateProxyCognitoUserPoolProxyApiuserPoolIdwellknownCE432EBE",
2057+
},
2058+
"PathPart": "jwks.json",
2059+
"RestApiId": {
2060+
"Ref": "CognitoPrivateProxyCognitoUserPoolProxyApi818B53B1",
2061+
},
2062+
},
2063+
"Type": "AWS::ApiGateway::Resource",
2064+
},
2065+
"CognitoPrivateProxyCognitoUserPoolProxyApiuserPoolIdwellknownjwksjsonGET21D71187": {
2066+
"Properties": {
2067+
"AuthorizationType": "NONE",
2068+
"HttpMethod": "GET",
2069+
"Integration": {
2070+
"IntegrationHttpMethod": "GET",
2071+
"IntegrationResponses": [
2072+
{
2073+
"StatusCode": "200",
2074+
},
2075+
],
2076+
"RequestParameters": {
2077+
"integration.request.path.userPoolId": "method.request.path.userPoolId",
2078+
},
2079+
"Type": "HTTP",
2080+
"Uri": "https://cognito-idp.us-east-1.amazonaws.com/{userPoolId}/.well-known/jwks.json",
2081+
},
2082+
"MethodResponses": [
2083+
{
2084+
"StatusCode": "200",
2085+
},
2086+
],
2087+
"RequestParameters": {
2088+
"method.request.path.userPoolId": true,
2089+
},
2090+
"ResourceId": {
2091+
"Ref": "CognitoPrivateProxyCognitoUserPoolProxyApiuserPoolIdwellknownjwksjsonD9640238",
2092+
},
2093+
"RestApiId": {
2094+
"Ref": "CognitoPrivateProxyCognitoUserPoolProxyApi818B53B1",
2095+
},
2096+
},
2097+
"Type": "AWS::ApiGateway::Method",
2098+
},
2099+
"CognitoPrivateProxyCognitoUserPoolProxyApiuserPoolIdwellknownjwksjsonOPTIONSA2D13B9E": {
2100+
"Properties": {
2101+
"ApiKeyRequired": false,
2102+
"AuthorizationType": "NONE",
2103+
"HttpMethod": "OPTIONS",
2104+
"Integration": {
2105+
"IntegrationResponses": [
2106+
{
2107+
"ResponseParameters": {
2108+
"method.response.header.Access-Control-Allow-Headers": "'amz-sdk-invocation-id,amz-sdk-request,cache-control,content-type,x-amz-target,x-amz-user-agent'",
2109+
"method.response.header.Access-Control-Allow-Methods": "'GET,POST,OPTIONS'",
2110+
"method.response.header.Access-Control-Allow-Origin": "'*'",
2111+
},
2112+
"StatusCode": "204",
2113+
},
2114+
],
2115+
"RequestTemplates": {
2116+
"application/json": "{ statusCode: 200 }",
2117+
},
2118+
"Type": "MOCK",
2119+
},
2120+
"MethodResponses": [
2121+
{
2122+
"ResponseParameters": {
2123+
"method.response.header.Access-Control-Allow-Headers": true,
2124+
"method.response.header.Access-Control-Allow-Methods": true,
2125+
"method.response.header.Access-Control-Allow-Origin": true,
2126+
},
2127+
"StatusCode": "204",
2128+
},
2129+
],
2130+
"ResourceId": {
2131+
"Ref": "CognitoPrivateProxyCognitoUserPoolProxyApiuserPoolIdwellknownjwksjsonD9640238",
2132+
},
2133+
"RestApiId": {
2134+
"Ref": "CognitoPrivateProxyCognitoUserPoolProxyApi818B53B1",
2135+
},
2136+
},
2137+
"Type": "AWS::ApiGateway::Method",
2138+
},
19392139
"CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F": {
19402140
"DependsOn": [
19412141
"CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092",
@@ -17224,6 +17424,26 @@ exports[`GenerativeAiUseCases matches the snapshot (closed network mode) 6`] = `
1722417424
"USER_POOL_ID": {
1722517425
"Ref": "AuthUserPool8115E87F",
1722617426
},
17427+
"USER_POOL_PROXY_ENDPOINT": {
17428+
"Fn::Join": [
17429+
"",
17430+
[
17431+
"https://",
17432+
{
17433+
"Fn::ImportValue": "ClosedNetworkStack:ExportsOutputRefCognitoPrivateProxyCognitoUserPoolProxyApi818B53B11EFC652B",
17434+
},
17435+
".execute-api.us-east-1.",
17436+
{
17437+
"Ref": "AWS::URLSuffix",
17438+
},
17439+
"/",
17440+
{
17441+
"Fn::ImportValue": "ClosedNetworkStack:ExportsOutputRefCognitoPrivateProxyCognitoUserPoolProxyApiDeploymentStageprod0C2BA9A5793BEF8B",
17442+
},
17443+
"/",
17444+
],
17445+
],
17446+
},
1722717447
"VIDEO_GENERATION_MODEL_IDS": {
1722817448
"Fn::Join": [
1722917449
"",
@@ -37620,6 +37840,7 @@ exports[`GenerativeAiUseCases matches the snapshot 6`] = `
3762037840
"USER_POOL_ID": {
3762137841
"Ref": "AuthUserPool8115E87F",
3762237842
},
37843+
"USER_POOL_PROXY_ENDPOINT": "",
3762337844
"VIDEO_GENERATION_MODEL_IDS": {
3762437845
"Fn::Join": [
3762537846
"",

0 commit comments

Comments
 (0)