Skip to content

Commit c0f6f09

Browse files
chore: add gen2 auth e2e infra (#5179)
* chore(infra): Api migrate to Gen 2 E2E * fix: git update-index --chmod=+x * fix: code review * chore: port auth backend * chore: port lambda triggers for create user and custom email sender * chore: move utils to infra-common * chore: compile infra common to js * chore: update utils for js restructure, add missing deps * chore: fix confirmation code infra * chore: update tests to run for gen2 stacks * chore: remove alias from custom sender lambda * chore: fix deliveryMedium in reset password test * chore: add phone sign in infra * chore: rename email-sign-in * chore: add license header * chore: fix formatting in GH workflow * chore: update package-lock * chore: remove deleted file * chore: fix formatting * chore: update package lock * chore: remove dup function * chore: remove changes from merge conflicts * chore: fix build script * chore: fetch auth amplify_outputs * chore: remove libgit2dart * chore: add custom sms sender * chore: add stack name to infra resources * chore: pull gen2 backend for authenticator * chore: update package-lock * chore: move dependencies to dev_dependencies * chore: update fetch auth session tests * chore: separate reset pw and confirmation delivery medium * chore: fix hanging test * chore: rename test group * chore: update comments, remove unused type * chore: update package lock --------- Co-authored-by: Elijah Quartey <quaelija@amazon.com>
1 parent c5a7100 commit c0f6f09

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+2686
-368
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# amplify
2+
node_modules
3+
.amplify
4+
amplify_outputs*
5+
amplifyconfiguration*
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import { defineAuth } from "@aws-amplify/backend";
5+
6+
/**
7+
* Define and configure your auth resource
8+
* @see https://docs.amplify.aws/gen2/build-a-backend/auth
9+
*/
10+
export const auth = defineAuth({
11+
loginWith: {
12+
email: true,
13+
},
14+
});
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import { defineBackend } from "@aws-amplify/backend";
5+
import { addAuthUserExtensions } from "infra-common";
6+
import { auth } from "./auth/resource";
7+
8+
const backend = defineBackend({
9+
auth,
10+
});
11+
12+
const resources = backend.auth.resources;
13+
const { userPool, cfnResources } = resources;
14+
const { stack } = userPool;
15+
const { cfnUserPool } = cfnResources;
16+
17+
// Adds infra for creating/deleting users via App Sync and fetching confirmation
18+
// and MFA codes from App Sync.
19+
const customOutputs = addAuthUserExtensions({
20+
name: "email-sign-in",
21+
stack,
22+
userPool,
23+
cfnUserPool,
24+
});
25+
backend.addOutput(customOutputs);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"type": "module"
3+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"compilerOptions": {
3+
"target": "es2022",
4+
"module": "es2022",
5+
"moduleResolution": "bundler",
6+
"resolveJsonModule": true,
7+
"esModuleInterop": true,
8+
"forceConsistentCasingInFileNames": true,
9+
"strict": true,
10+
"skipLibCheck": true,
11+
"paths": {
12+
"$amplify/*": [
13+
"../.amplify/generated/*"
14+
]
15+
}
16+
}
17+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"name": "email-sign-in",
3+
"version": "1.0.0",
4+
"main": "index.js"
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# amplify
2+
node_modules
3+
.amplify
4+
amplify_outputs*
5+
amplifyconfiguration*
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import { defineAuth } from "@aws-amplify/backend";
5+
6+
/**
7+
* Define and configure your auth resource
8+
* @see https://docs.amplify.aws/gen2/build-a-backend/auth
9+
*/
10+
export const auth = defineAuth({
11+
loginWith: {
12+
phone: true,
13+
},
14+
});
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import { defineBackend } from "@aws-amplify/backend";
5+
import { addAuthUserExtensions } from "infra-common";
6+
import { auth } from "./auth/resource";
7+
8+
const backend = defineBackend({
9+
auth,
10+
});
11+
12+
const resources = backend.auth.resources;
13+
const { userPool, cfnResources } = resources;
14+
const { stack } = userPool;
15+
const { cfnUserPool } = cfnResources;
16+
17+
// Adds infra for creating/deleting users via App Sync and fetching confirmation
18+
// and MFA codes from App Sync.
19+
const customOutputs = addAuthUserExtensions({
20+
name: "phone-sign-in",
21+
stack,
22+
userPool,
23+
cfnUserPool,
24+
});
25+
backend.addOutput(customOutputs);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"type": "module"
3+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"compilerOptions": {
3+
"target": "es2022",
4+
"module": "es2022",
5+
"moduleResolution": "bundler",
6+
"resolveJsonModule": true,
7+
"esModuleInterop": true,
8+
"forceConsistentCasingInFileNames": true,
9+
"strict": true,
10+
"skipLibCheck": true,
11+
"paths": {
12+
"$amplify/*": [
13+
"../.amplify/generated/*"
14+
]
15+
}
16+
}
17+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"name": "phone-sign-in",
3+
"version": "1.0.0",
4+
"main": "index.js"
5+
}

infra-gen2/infra-common/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
".": "./dist/index.js"
88
},
99
"scripts": {
10-
"build": "tsc"
10+
"build": "tsc && npm run copy-graphql",
11+
"copy-graphql": "cp -r ./src/schemas ./dist"
1112
}
1213
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# auth-user-extensions
2+
3+
This directory contains extensions useful for managing authorized users, including:
4+
5+
- creating & deleting users via App Sync
6+
- sending confirmation codes to App Sync instead of email/SMS
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { BackendBase } from "@aws-amplify/backend";
2+
import { Stack } from "aws-cdk-lib";
3+
import { CfnUserPool, IUserPool } from "aws-cdk-lib/aws-cognito";
4+
import { addCreateUserLambda } from "./create-user-lambda";
5+
import { addCustomSenderLambda } from "./custom-sender-lambda";
6+
import { addDeleteUserLambda } from "./delete-user-lambda";
7+
import { addUserGraphql } from "./user-graphql";
8+
9+
type AmplifyOutputs = Parameters<BackendBase["addOutput"]>[0];
10+
11+
export const addAuthUserExtensions = ({
12+
name,
13+
stack,
14+
userPool,
15+
cfnUserPool,
16+
}: {
17+
name: string;
18+
stack: Stack;
19+
userPool: IUserPool;
20+
cfnUserPool: CfnUserPool;
21+
}): AmplifyOutputs => {
22+
const graphQL = addUserGraphql(stack);
23+
addCustomSenderLambda({ name, stack, cfnUserPool, graphQL });
24+
addCreateUserLambda({ name, stack, userPool, graphQL });
25+
addDeleteUserLambda({ name, stack, userPool, graphQL });
26+
return {
27+
data: {
28+
aws_region: stack.region,
29+
url: graphQL.graphqlUrl,
30+
api_key: graphQL.apiKey,
31+
default_authorization_type: "API_KEY",
32+
authorization_types: [],
33+
},
34+
};
35+
};
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import { Stack } from "aws-cdk-lib";
5+
import { GraphqlApi, MappingTemplate } from "aws-cdk-lib/aws-appsync";
6+
import { IUserPool } from "aws-cdk-lib/aws-cognito";
7+
import { Runtime } from "aws-cdk-lib/aws-lambda";
8+
import { NodejsFunction } from "aws-cdk-lib/aws-lambda-nodejs";
9+
import path from "path";
10+
11+
export function addCreateUserLambda({
12+
name,
13+
stack,
14+
graphQL,
15+
userPool,
16+
}: {
17+
name: string;
18+
stack: Stack;
19+
graphQL: GraphqlApi;
20+
userPool: IUserPool;
21+
}) {
22+
const createUserLambda = new NodejsFunction(stack, `${name}-createUser`, {
23+
entry: path.resolve(__dirname, "..", "lambda-triggers", "create-user.js"),
24+
runtime: Runtime.NODEJS_18_X,
25+
environment: {
26+
USER_POOL_ID: userPool.userPoolId,
27+
},
28+
});
29+
userPool.grant(
30+
createUserLambda,
31+
"cognito-idp:AdminCreateUser",
32+
"cognito-idp:AdminSetUserPassword",
33+
"cognito-idp:AdminSetUserMFAPreference",
34+
"cognito-idp:AdminUpdateUserAttributes"
35+
);
36+
const createUserSource = graphQL.addLambdaDataSource(
37+
`${name}-GraphQLApiCreateUserLambda`,
38+
createUserLambda
39+
);
40+
createUserSource.createResolver(`${name}-MutationCreateUserResolver`, {
41+
typeName: "Mutation",
42+
fieldName: "createUser",
43+
requestMappingTemplate: MappingTemplate.lambdaRequest(),
44+
responseMappingTemplate: MappingTemplate.lambdaResult(),
45+
});
46+
}
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import { RemovalPolicy, Stack } from "aws-cdk-lib";
5+
import {
6+
Assign,
7+
GraphqlApi,
8+
MappingTemplate,
9+
PrimaryKey,
10+
Values,
11+
} from "aws-cdk-lib/aws-appsync";
12+
import { CfnUserPool } from "aws-cdk-lib/aws-cognito";
13+
import { AttributeType, BillingMode, Table } from "aws-cdk-lib/aws-dynamodb";
14+
import { ServicePrincipal } from "aws-cdk-lib/aws-iam";
15+
import { Key } from "aws-cdk-lib/aws-kms";
16+
import { Runtime } from "aws-cdk-lib/aws-lambda";
17+
import { NodejsFunction } from "aws-cdk-lib/aws-lambda-nodejs";
18+
import path from "path";
19+
20+
export function addCustomSenderLambda({
21+
name,
22+
stack,
23+
graphQL,
24+
cfnUserPool,
25+
}: {
26+
name: string;
27+
stack: Stack;
28+
graphQL: GraphqlApi;
29+
cfnUserPool: CfnUserPool;
30+
}): NodejsFunction {
31+
const customSenderKmsKey = new Key(stack, `${name}-CustomSenderKey`, {
32+
description: `Key for encrypting/decrypting SMS messages sent from ${stack.stackId}`,
33+
removalPolicy: RemovalPolicy.DESTROY,
34+
});
35+
36+
const customEmailSender = new NodejsFunction(
37+
stack,
38+
`${name}-customEmailSender`,
39+
{
40+
entry: path.resolve(
41+
__dirname,
42+
"..",
43+
"lambda-triggers",
44+
"custom-email-sender.js"
45+
),
46+
runtime: Runtime.NODEJS_18_X,
47+
bundling: {
48+
nodeModules: ["@aws-crypto/client-node"],
49+
},
50+
environment: {
51+
GRAPHQL_API_ENDPOINT: graphQL.graphqlUrl,
52+
GRAPHQL_API_KEY: graphQL.apiKey!,
53+
KMS_KEY_ARN: customSenderKmsKey.keyArn,
54+
},
55+
}
56+
);
57+
58+
customEmailSender.addPermission("PermitCognitoInvoke", {
59+
principal: new ServicePrincipal("cognito-idp.amazonaws.com"),
60+
sourceArn: cfnUserPool.attrArn,
61+
});
62+
63+
const customSmsSender = new NodejsFunction(stack, `${name}-customSmsSender`, {
64+
entry: path.resolve(
65+
__dirname,
66+
"..",
67+
"lambda-triggers",
68+
"custom-sms-sender.js"
69+
),
70+
runtime: Runtime.NODEJS_18_X,
71+
bundling: {
72+
nodeModules: ["@aws-crypto/client-node"],
73+
},
74+
environment: {
75+
GRAPHQL_API_ENDPOINT: graphQL.graphqlUrl,
76+
GRAPHQL_API_KEY: graphQL.apiKey!,
77+
KMS_KEY_ARN: customSenderKmsKey.keyArn,
78+
},
79+
});
80+
81+
customSmsSender.addPermission("PermitCognitoInvoke", {
82+
principal: new ServicePrincipal("cognito-idp.amazonaws.com"),
83+
sourceArn: cfnUserPool.attrArn,
84+
});
85+
86+
cfnUserPool.lambdaConfig = {
87+
customEmailSender: {
88+
lambdaArn: customEmailSender.functionArn,
89+
lambdaVersion: "V1_0",
90+
},
91+
customSmsSender: {
92+
lambdaArn: customSmsSender.functionArn,
93+
lambdaVersion: "V1_0",
94+
},
95+
kmsKeyId: customSenderKmsKey.keyArn,
96+
};
97+
98+
graphQL.grantMutation(customEmailSender);
99+
customSenderKmsKey.grantDecrypt(customEmailSender);
100+
101+
graphQL.grantMutation(customSmsSender);
102+
customSenderKmsKey.grantDecrypt(customSmsSender);
103+
104+
const mfaCodesTable = new Table(stack, `${name}-MFACodesTable`, {
105+
removalPolicy: RemovalPolicy.DESTROY,
106+
billingMode: BillingMode.PAY_PER_REQUEST,
107+
partitionKey: {
108+
type: AttributeType.STRING,
109+
name: "username",
110+
},
111+
sortKey: {
112+
type: AttributeType.STRING,
113+
name: "code",
114+
},
115+
});
116+
117+
const mfaCodesSource = graphQL.addDynamoDbDataSource(
118+
"GraphQLApiMFACodes",
119+
mfaCodesTable
120+
);
121+
122+
// Mutation.createMFACode
123+
mfaCodesSource.createResolver(`${name}-MutationCreateMFACodeResolver`, {
124+
typeName: "Mutation",
125+
fieldName: "createMFACode",
126+
requestMappingTemplate: MappingTemplate.dynamoDbPutItem(
127+
new PrimaryKey(
128+
new Assign("username", "$input.username"),
129+
new Assign("code", "$input.code")
130+
),
131+
Values.projecting("input")
132+
),
133+
responseMappingTemplate: MappingTemplate.dynamoDbResultItem(),
134+
});
135+
136+
// Query.listMFACodes
137+
mfaCodesSource.createResolver(`${name}-QueryListMFACodesResolver`, {
138+
typeName: "Query",
139+
fieldName: "listMFACodes",
140+
requestMappingTemplate: MappingTemplate.dynamoDbScanTable(),
141+
responseMappingTemplate: MappingTemplate.dynamoDbResultItem(),
142+
});
143+
144+
return customEmailSender;
145+
}

0 commit comments

Comments
 (0)