Skip to content

Commit 6549870

Browse files
committed
JavaScript (v3): Cognito - Complete functionality and README for AutoConfirm workflow.
1 parent 870821c commit 6549870

File tree

21 files changed

+1246
-18
lines changed

21 files changed

+1246
-18
lines changed

.doc_gen/metadata/cognito-identity-provider_metadata.yaml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,14 @@ cognito-identity-provider_DeleteUser:
753753
snippet_tags:
754754
- gov2.cognito-identity-provider.CognitoActions.struct
755755
- gov2.cognito-identity-provider.DeleteUser
756+
JavaScript:
757+
versions:
758+
- sdk_version: 3
759+
github: javascriptv3/example_code/cross-services/wkflw-pools-triggers
760+
excerpts:
761+
- description:
762+
snippet_tags:
763+
- javascript.v3.cognito-idp.actions.DeleteUser
756764
services:
757765
cognito-identity-provider: {DeleteUser}
758766
cognito-identity-provider_CreateUserPool:
@@ -793,6 +801,15 @@ cognito-identity-provider_UpdateUserPool:
793801
snippet_tags:
794802
- gov2.cognito-identity-provider.CognitoActions.struct
795803
- gov2.cognito-identity-provider.UpdateUserPool
804+
JavaScript:
805+
versions:
806+
- sdk_version: 3
807+
github: javascriptv3/example_code/cross-services/wkflw-pools-triggers
808+
sdkguide:
809+
excerpts:
810+
- description:
811+
snippet_tags:
812+
- javascript.v3.cognito-idp.actions.UpdateUserPool
796813
services:
797814
cognito-identity-provider: {UpdateUserPool}
798815
cognito-identity-provider_ForgotPassword:

.doc_gen/metadata/cross_metadata.yaml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -822,6 +822,30 @@ cross_CognitoAutoConfirmUser:
822822
- description: Clean up resources.
823823
snippet_tags:
824824
- gov2.cognito-identity-provider.Resources.complete
825+
JavaScript:
826+
versions:
827+
- sdk_version: 3
828+
github: javascriptv3/example_code/cross-services/wkflw-pools-triggers
829+
sdk_guide:
830+
excerpts:
831+
- description: Run an interactive scenario at a command prompt.
832+
snippet_tags:
833+
- javascript.v3.wkflw.pools-triggers.index
834+
- javascript.v3.wkflw.pools-triggers.AutoConfirm
835+
- javascript.v3.wkflw.pools-triggers.common
836+
- description: Handle the <code>PreSignUp</code> trigger with a &LAM; function.
837+
snippet_tags:
838+
- javascript.v3.wkflw.pools-triggers.handler.AutoConfirm
839+
- description: &CWL; actions
840+
snippet_files:
841+
- javascriptv3/example_code/cross-services/wkflw-pools-triggers/actions/cloudwatch-logs-actions.js
842+
- description: &COG; actions
843+
snippet_files:
844+
- javascriptv3/example_code/cross-services/wkflw-pools-triggers/actions/cognito-actions.js
845+
- description: &DDB; actions
846+
snippet_files:
847+
- javascriptv3/example_code/cross-services/wkflw-pools-triggers/actions/dynamodb-actions.js
848+
825849
services:
826850
cognito-identity-provider: {UpdateUserPool, SignUp, InitiateAuth, DeleteUser}
827851
lambda: {}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# Customize Amazon Cognito authentication behavior with Lambda functions
2+
3+
## Overview
4+
5+
This example shows how to use AWS SDKs to customize Amazon Cognito authentication behavior. You can configure
6+
your Amazon Cognito user pool to automatically invoke AWS Lambda functions at various points in the authentication
7+
process, such as before sign-up, during sign-in, and after authentication.
8+
9+
There are three workflows demonstrated by this example:
10+
11+
* Automatically confirm and verify the email of known users by using a pre sign-up trigger.
12+
* [Not yet implemented] Automatically add known users at sign-in by using a migrate user trigger.
13+
* [Not yet implemented] Write custom information to an Amazon DynamoDB table after users are authenticated by using a post authentication trigger.
14+
15+
These workflows are described in more detail in the main [README](../../../../workflows/user_pools_and_lambda_triggers/README.md)
16+
for these examples.
17+
18+
## Automatically confirm known users
19+
20+
A [pre sign-up Lambda trigger](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-pre-sign-up.html)
21+
is invoked when a user starts the sign-up process and lets your Lambda function
22+
take action before Amazon Cognito adds the user to the user pool.
23+
24+
## Automatically migrate known users
25+
26+
A [migrate user Lambda trigger](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-migrate-user.html)
27+
is invoked when a user doesn't exist in the user pool at sign-in with a password.
28+
After the Lambda function returns successfully, Amazon Cognito creates the user in the user pool.
29+
30+
## Write custom activity after authentication
31+
32+
A [post authentication Lambda trigger](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-post-authentication.html)
33+
is invoked after signing in a user, so you can add custom logic after Amazon Cognito authenticates the user.
34+
35+
## ⚠ Important
36+
37+
* Running this code might result in charges to your AWS account.
38+
* Running the tests might result in charges to your AWS account.
39+
* We recommend that you grant your code least privilege. At most, grant only the minimum permissions required to perform the task. For more information, see [Grant least privilege](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege).
40+
* This code is not tested in every AWS Region. For more information, see [AWS Regional Services](https://aws.amazon.com/about-aws/global-infrastructure/regional-product-services).
41+
42+
## Run the examples
43+
44+
### Prerequisites
45+
46+
For general prerequisites, see the [README](../../../README.md#prerequisites) in the `javascriptv3` folder.
47+
48+
### Setup
49+
50+
This example deploys several resources by using an AWS CloudFormation stack. This stack
51+
deploys the following resources:
52+
53+
* An Amazon DynamoDB table named `doc-example-custom-users` that has a `UserEmail` primary key.
54+
This table functions as an external user store.
55+
* An Amazon Cognito user pool that requires an email, sends an email with verification code
56+
when a new user is added, does not require MFA, and allows account recovery with a verified email.
57+
* An Amazon Cognito client application. This is required for client calls to sign-up and
58+
authentication users.
59+
* An AWS Identity and Access Management (IAM) role that can be assumed by Lambda.
60+
This role grants permission to Lambda to read from and write to the DynamoDB table and
61+
write to CloudWatch Logs.
62+
63+
### Deploy AWS resources
64+
65+
The AWS resources for this example are deployed by using the AWS Cloud Development Kit (AWS CDK).
66+
67+
To install the AWS CDK, follow the instructions in the
68+
[Developer Guide](https://docs.aws.amazon.com/cdk/v2/guide/home.html).
69+
70+
Deploy resources at a command prompt from the [cdk](./cdk/) folder:
71+
72+
```
73+
npm install
74+
cdk deploy
75+
```
76+
77+
### Instructions
78+
79+
Run `./index --help` for instructions on running a scenario.
80+
81+
### Cleanup
82+
83+
Delete resources deployed for this example by deleting the stack.
84+
85+
Delete the stack at a command prompt from the [cdk](./cdk/) folder:
86+
87+
```
88+
cdk destroy
89+
```
90+
91+
## Additional resources
92+
93+
- [Amazon Cognito Identity Provider Developer Guide](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools.html)
94+
- [Amazon Cognito Identity Provider API Reference](https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/Welcome.html)
95+
- [SDK for JavaScript V3 Amazon Cognito Identity Provider reference](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/cognito-identity-provider/)
96+
97+
---
98+
99+
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. SPDX-License-Identifier: Apache-2.0
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import {
5+
CloudWatchLogsClient,
6+
GetLogEventsCommand,
7+
OrderBy,
8+
paginateDescribeLogStreams,
9+
} from "@aws-sdk/client-cloudwatch-logs";
10+
11+
/**
12+
* Get the latest log stream for a Lambda function.
13+
* @param {{ functionName: string, region: string }} config
14+
* @returns {Promise<[import("@aws-sdk/client-cloudwatch-logs").LogStream | null, unknown]>}
15+
*/
16+
export const getLatestLogStreamForLambda = async ({ functionName, region }) => {
17+
try {
18+
const logGroupName = `/aws/lambda/${functionName}`;
19+
const cwlClient = new CloudWatchLogsClient({ region });
20+
const paginator = paginateDescribeLogStreams(
21+
{ client: cwlClient },
22+
{
23+
descending: true,
24+
limit: 1,
25+
orderBy: OrderBy.LastEventTime,
26+
logGroupName,
27+
},
28+
);
29+
30+
for await (const page of paginator) {
31+
return [page.logStreams[0], null];
32+
}
33+
} catch (err) {
34+
return [null, err];
35+
}
36+
};
37+
38+
/**
39+
* Get the log events for a Lambda function's log stream.
40+
* @param {{
41+
* functionName: string,
42+
* logStreamName: string,
43+
* eventCount: number,
44+
* region: string
45+
* }} config
46+
* @returns {Promise<[import("@aws-sdk/client-cloudwatch-logs").OutputLogEvent[] | null, unknown]>}
47+
*/
48+
export const getLogEvents = async ({
49+
functionName,
50+
logStreamName,
51+
eventCount,
52+
region,
53+
}) => {
54+
try {
55+
const cwlClient = new CloudWatchLogsClient({ region });
56+
const logGroupName = `/aws/lambda/${functionName}`;
57+
const response = await cwlClient.send(
58+
new GetLogEventsCommand({
59+
logStreamName: logStreamName,
60+
limit: eventCount,
61+
logGroupName: logGroupName,
62+
}),
63+
);
64+
65+
return [response.events, null];
66+
} catch (err) {
67+
return [null, err];
68+
}
69+
};
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import {
5+
AdminGetUserCommand,
6+
CognitoIdentityProviderClient,
7+
DeleteUserCommand,
8+
InitiateAuthCommand,
9+
SignUpCommand,
10+
UpdateUserPoolCommand,
11+
} from "@aws-sdk/client-cognito-identity-provider";
12+
13+
// snippet-start:[javascript.v3.cognito-idp.actions.UpdateUserPool]
14+
/**
15+
* Connect a Lambda function to the PreSignUp trigger for a Cognito user pool
16+
* @param {{ region: string, userPoolId: string, handlerArn: string }} config
17+
* @returns {Promise<[import("@aws-sdk/client-cognito-identity-provider").UpdateUserPoolCommandOutput | null, unknown]>}
18+
*/
19+
export const addPreSignUpHandler = async ({
20+
region,
21+
userPoolId,
22+
handlerArn,
23+
}) => {
24+
try {
25+
const cognitoClient = new CognitoIdentityProviderClient({
26+
region,
27+
});
28+
29+
const command = new UpdateUserPoolCommand({
30+
UserPoolId: userPoolId,
31+
LambdaConfig: {
32+
PreSignUp: handlerArn,
33+
},
34+
});
35+
36+
const response = await cognitoClient.send(command);
37+
return [response, null];
38+
} catch (err) {
39+
return [null, err];
40+
}
41+
};
42+
// snippet-end:[javascript.v3.cognito-idp.actions.UpdateUserPool]
43+
44+
/**
45+
* Attempt to register a user to a user pool with a given username and password.
46+
* @param {{
47+
* region: string,
48+
* userPoolClientId: string,
49+
* username: string,
50+
* email: string,
51+
* password: string
52+
* }} config
53+
* @returns {Promise<[import("@aws-sdk/client-cognito-identity-provider").SignUpCommandOutput | null, unknown]>}
54+
*/
55+
export const signUpUser = async ({
56+
region,
57+
userPoolClientId,
58+
username,
59+
email,
60+
password,
61+
}) => {
62+
try {
63+
const cognitoClient = new CognitoIdentityProviderClient({
64+
region,
65+
});
66+
67+
const response = await cognitoClient.send(
68+
new SignUpCommand({
69+
ClientId: userPoolClientId,
70+
Username: username,
71+
Password: password,
72+
UserAttributes: [{ Name: "email", Value: email }],
73+
}),
74+
);
75+
return [response, null];
76+
} catch (err) {
77+
return [null, err];
78+
}
79+
};
80+
81+
/**
82+
* Sign in a user to Amazon Cognito using a username and password authentication flow.
83+
* @param {{ region: string, clientId: string, username: string, password: string }} config
84+
* @returns {Promise<[import("@aws-sdk/client-cognito-identity-provider").InitiateAuthCommandOutput | null, unknown]>}
85+
*/
86+
export const signIn = async ({ region, clientId, username, password }) => {
87+
try {
88+
const cognitoClient = new CognitoIdentityProviderClient({ region });
89+
const response = await cognitoClient.send(
90+
new InitiateAuthCommand({
91+
AuthFlow: "USER_PASSWORD_AUTH",
92+
ClientId: clientId,
93+
AuthParameters: { USERNAME: username, PASSWORD: password },
94+
}),
95+
);
96+
return [response, null];
97+
} catch (err) {
98+
return [null, err];
99+
}
100+
};
101+
102+
/**
103+
* Retrieve an existing user from a user pool.
104+
* @param {{ region: string, userPoolId: string, username: string }} config
105+
* @returns {Promise<[import("@aws-sdk/client-cognito-identity-provider").AdminGetUserCommandOutput | null, unknown]>}
106+
*/
107+
export const getUser = async ({ region, userPoolId, username }) => {
108+
try {
109+
const cognitoClient = new CognitoIdentityProviderClient({ region });
110+
const response = await cognitoClient.send(
111+
new AdminGetUserCommand({
112+
UserPoolId: userPoolId,
113+
Username: username,
114+
}),
115+
);
116+
return [response, null];
117+
} catch (err) {
118+
return [null, err];
119+
}
120+
};
121+
122+
// snippet-start:[javascript.v3.cognito-idp.actions.DeleteUser]
123+
/**
124+
* Delete the signed-in user. Useful for allowing a user to delete their
125+
* own profile.
126+
* @param {{ region: string, accessToken: string }} config
127+
* @returns {Promise<[import("@aws-sdk/client-cognito-identity-provider").DeleteUserCommandOutput | null, unknown]>}
128+
*/
129+
export const deleteUser = async ({ region, accessToken }) => {
130+
try {
131+
const client = new CognitoIdentityProviderClient({ region });
132+
const response = await client.send(
133+
new DeleteUserCommand({ AccessToken: accessToken }),
134+
);
135+
return [response, null];
136+
} catch (err) {
137+
return [null, err];
138+
}
139+
};
140+
// snippet-end:[javascript.v3.cognito-idp.actions.DeleteUser]

0 commit comments

Comments
 (0)