Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: migrate sms only MFA infra to Gen 2 #5291

Merged
merged 11 commits into from
Aug 14, 2024
5 changes: 5 additions & 0 deletions infra-gen2/backends/auth/mfa-optional-sms/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# amplify
node_modules
.amplify
amplify_outputs*
amplifyconfiguration*
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import { defineAuth } from "@aws-amplify/backend";

export const auth = defineAuth({
loginWith: {
email: true,
},
multifactor: {
mode: "OPTIONAL",
sms: true,
},
});
25 changes: 25 additions & 0 deletions infra-gen2/backends/auth/mfa-optional-sms/amplify/backend.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import { defineBackend } from "@aws-amplify/backend";
import { addAuthUserExtensions } from "infra-common";
import { auth } from "./auth/resource";

const backend = defineBackend({
auth,
});

const resources = backend.auth.resources;
const { userPool, cfnResources } = resources;
const { stack } = userPool;
const { cfnUserPool } = cfnResources;

// Adds infra for creating/deleting users via App Sync and fetching confirmation
// and MFA codes from App Sync.
const customOutputs = addAuthUserExtensions({
name: "mfa-optional-sms",
stack,
userPool,
cfnUserPool,
});
backend.addOutput(customOutputs);
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"type": "module"
}
17 changes: 17 additions & 0 deletions infra-gen2/backends/auth/mfa-optional-sms/amplify/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"compilerOptions": {
"target": "es2022",
"module": "es2022",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"paths": {
"$amplify/*": [
"../.amplify/generated/*"
]
}
}
}
5 changes: 5 additions & 0 deletions infra-gen2/backends/auth/mfa-optional-sms/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "mfa-optional-sms",
"version": "1.0.0",
"main": "index.js"
}
5 changes: 5 additions & 0 deletions infra-gen2/backends/auth/mfa-required-sms/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# amplify
node_modules
.amplify
amplify_outputs*
amplifyconfiguration*
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import { defineAuth } from "@aws-amplify/backend";

export const auth = defineAuth({
loginWith: {
email: true,
},
multifactor: {
mode: "REQUIRED",
sms: true,
},
});
25 changes: 25 additions & 0 deletions infra-gen2/backends/auth/mfa-required-sms/amplify/backend.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import { defineBackend } from "@aws-amplify/backend";
import { addAuthUserExtensions } from "infra-common";
import { auth } from "./auth/resource";

const backend = defineBackend({
auth,
});

const resources = backend.auth.resources;
const { userPool, cfnResources } = resources;
const { stack } = userPool;
const { cfnUserPool } = cfnResources;

// Adds infra for creating/deleting users via App Sync and fetching confirmation
// and MFA codes from App Sync.
const customOutputs = addAuthUserExtensions({
name: "mfa-required-sms",
stack,
userPool,
cfnUserPool,
});
backend.addOutput(customOutputs);
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"type": "module"
}
17 changes: 17 additions & 0 deletions infra-gen2/backends/auth/mfa-required-sms/amplify/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"compilerOptions": {
"target": "es2022",
"module": "es2022",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"paths": {
"$amplify/*": [
"../.amplify/generated/*"
]
}
}
}
5 changes: 5 additions & 0 deletions infra-gen2/backends/auth/mfa-required-sms/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "mfa-required-sms",
"version": "1.0.0",
"main": "index.js"
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { CfnUserPool, IUserPool } from "aws-cdk-lib/aws-cognito";
import { addCreateUserLambda } from "./create-user-lambda";
import { addCustomSenderLambda } from "./custom-sender-lambda";
import { addDeleteUserLambda } from "./delete-user-lambda";
import { addEnableSmsMfaLambda } from "./enable-sms-mfa-lambda";
import { addUserGraphql } from "./user-graphql";

type AmplifyOutputs = Parameters<BackendBase["addOutput"]>[0];
Expand All @@ -23,6 +24,7 @@ export const addAuthUserExtensions = ({
addCustomSenderLambda({ name, stack, cfnUserPool, graphQL });
addCreateUserLambda({ name, stack, userPool, graphQL });
addDeleteUserLambda({ name, stack, userPool, graphQL });
addEnableSmsMfaLambda({ name, stack, userPool, graphQL });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: Will this add the lambda to every backend that calls this method? Can this be optional?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it will add it to each env. It could be optional. So could the other lambdas. I think it is simpler to add all the lambdas to each env rather than try to remember which envs have which lambda. It doesn't really cost anything if it doesn't get invoked.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't need to come from this PR, but it would be a nice optimization to have these be pulled in as needed. Besides reducing the amount of resources added to the AWS account, it may speed up deployment of the entire infra stack. May also prevent maintenance confusion by having the bare minimum for each env.

return {
data: {
aws_region: stack.region,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,6 @@ export function addCustomSenderLambda({
"custom-email-sender.js"
),
runtime: Runtime.NODEJS_18_X,
bundling: {
nodeModules: ["@aws-crypto/client-node"],
},
Comment on lines -47 to -49
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: This was causing an issue deploying the infra, and is not needed. It am not sure why it was not causing an issue previously.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the clean up.

environment: {
GRAPHQL_API_ENDPOINT: graphQL.graphqlUrl,
GRAPHQL_API_KEY: graphQL.apiKey!,
Expand All @@ -68,9 +65,6 @@ export function addCustomSenderLambda({
"custom-sms-sender.js"
),
runtime: Runtime.NODEJS_18_X,
bundling: {
nodeModules: ["@aws-crypto/client-node"],
},
environment: {
GRAPHQL_API_ENDPOINT: graphQL.graphqlUrl,
GRAPHQL_API_KEY: graphQL.apiKey!,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import { Stack } from "aws-cdk-lib";
import { GraphqlApi, MappingTemplate } from "aws-cdk-lib/aws-appsync";
import { IUserPool } from "aws-cdk-lib/aws-cognito";
import { Runtime } from "aws-cdk-lib/aws-lambda";
import { NodejsFunction } from "aws-cdk-lib/aws-lambda-nodejs";
import path from "path";

export function addEnableSmsMfaLambda({
name,
stack,
graphQL,
userPool,
}: {
name: string;
stack: Stack;
graphQL: GraphqlApi;
userPool: IUserPool;
}) {
const enableSmsMfaLambda = new NodejsFunction(stack, `${name}-enableSmsMfa`, {
runtime: Runtime.NODEJS_18_X,
entry: path.resolve(
__dirname,
"..",
"lambda-triggers",
"enable-sms-mfa.js"
),
environment: {
USER_POOL_ID: userPool.userPoolId,
},
});

userPool.grant(enableSmsMfaLambda, "cognito-idp:AdminSetUserMFAPreference");

// Mutation.enableSmsMfa
const enableSmsMfaSource = graphQL.addLambdaDataSource(
"GraphQLApiEnableSmsMfaLambda",
enableSmsMfaLambda
);

enableSmsMfaSource.createResolver("MutationEnableSmsMfaResolver", {
typeName: "Mutation",
fieldName: "enableSmsMfa",
requestMappingTemplate: MappingTemplate.lambdaRequest(),
responseMappingTemplate: MappingTemplate.lambdaResult(),
});
}
54 changes: 54 additions & 0 deletions infra-gen2/infra-common/src/lambda-triggers/enable-sms-mfa.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import * as cognito from "@aws-sdk/client-cognito-identity-provider";
import type * as lambda from "aws-lambda";

interface EnableSmsMfaRequest {
username: string;
}

interface EnableSmsMfaResponse {
success: boolean;
error?: string;
}

const USER_POOL_ID = process.env.USER_POOL_ID;
const CLIENT = new cognito.CognitoIdentityProviderClient({
region: process.env.REGION,
});

export const handler: lambda.AppSyncResolverHandler<
EnableSmsMfaRequest,
EnableSmsMfaResponse
> = async (
event: lambda.AppSyncResolverEvent<EnableSmsMfaRequest>
): Promise<EnableSmsMfaResponse> => {
console.log(`Got event: ${JSON.stringify(event, null, 2)}`);

const { username } = event.arguments;
console.log(`Enabling SMS MFA for user ${username}...`);
try {
const mfaParams: cognito.AdminSetUserMFAPreferenceCommandInput = {
UserPoolId: USER_POOL_ID,
Username: username,
SMSMfaSettings: {
Enabled: true,
PreferredMfa: true,
},
};
const resp = await CLIENT.send(
new cognito.AdminSetUserMFAPreferenceCommand(mfaParams),
);
console.log(`Successfully enabled MFA for ${username}`, resp);
return {
success: true,
};
} catch (err: any) {
console.log(`Could not enable MFA for ${username}`, err);
return {
success: false,
error: err.toString(),
};
}
};
14 changes: 14 additions & 0 deletions infra-gen2/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions infra-gen2/tool/deploy_gen2.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,16 @@ const List<AmplifyBackendGroup> infraConfig = [
identifier: 'phone-sign-in',
pathToSource: 'infra-gen2/backends/auth/phone-sign-in',
),
AmplifyBackend(
name: 'mfa-optional-sms',
identifier: 'mfa-opt-sms',
pathToSource: 'infra-gen2/backends/auth/mfa-optional-sms',
),
AmplifyBackend(
name: 'mfa-required-sms',
identifier: 'mfa-req-sms',
pathToSource: 'infra-gen2/backends/auth/mfa-required-sms',
),
],
),
AmplifyBackendGroup(
Expand Down
Loading
Loading