Skip to content

Commit 31250f3

Browse files
authored
Add Cognito Post Confirmation example (#884)
- Show how to work with Cognito's Post Confirmation events. - Make response generic so customers can return a different object as reponse. Signed-off-by: David Calavera <[email protected]>
1 parent 00d822e commit 31250f3

File tree

5 files changed

+108
-2
lines changed

5 files changed

+108
-2
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/target
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
[package]
2+
name = "basic-cognito-post-confirmation"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
# Starting in Rust 1.62 you can use `cargo add` to add dependencies
7+
# to your project.
8+
#
9+
# If you're using an older Rust version,
10+
# download cargo-edit(https://github.com/killercup/cargo-edit#installation)
11+
# to install the `add` subcommand.
12+
#
13+
# Running `cargo add DEPENDENCY_NAME` will
14+
# add the latest version of a dependency to the list,
15+
# and it will keep the alphabetic ordering for you.
16+
17+
[dependencies]
18+
aws-config = "1.5.0"
19+
aws-sdk-ses = "1.28.0"
20+
aws_lambda_events = { path = "../../lambda-events", default-features = false, features = ["cognito"] }
21+
22+
lambda_runtime = { path = "../../lambda-runtime" }
23+
tokio = { version = "1", features = ["macros"] }
24+
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Cognito Post Confirmation Request example
2+
3+
This example shows how to write a Lambda function in Rust to process Cognito's Post Confirmation requests.
4+
5+
This is a translation of the example in the AWS Docs to Rust: https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-post-confirmation.html#aws-lambda-triggers-post-confirmation-example
6+
7+
## Build & Deploy
8+
9+
1. Install [cargo-lambda](https://github.com/cargo-lambda/cargo-lambda#installation)
10+
2. Build the function with `cargo lambda build --release`
11+
3. Deploy the function to AWS Lambda with `cargo lambda deploy`
12+
13+
## Build for ARM 64
14+
15+
Build the function with `cargo lambda build --release --arm64`
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
use aws_config::BehaviorVersion;
2+
use aws_lambda_events::event::cognito::CognitoEventUserPoolsPostConfirmation;
3+
use aws_sdk_ses::{
4+
types::{Body, Content, Destination, Message},
5+
Client,
6+
};
7+
use lambda_runtime::{run, service_fn, tracing, Error, LambdaEvent};
8+
9+
const SOURCE_EMAIL: &str = "<source_email>";
10+
11+
async fn function_handler(
12+
client: &aws_sdk_ses::Client,
13+
event: LambdaEvent<CognitoEventUserPoolsPostConfirmation>,
14+
) -> Result<CognitoEventUserPoolsPostConfirmation, Error> {
15+
let payload = event.payload;
16+
17+
if let Some(email) = payload.request.user_attributes.get("email") {
18+
let body = if let Some(name) = payload.request.user_attributes.get("name") {
19+
format!("Welcome {name}, you have been confirmed.")
20+
} else {
21+
"Welcome, you have been confirmed.".to_string()
22+
};
23+
send_post_confirmation_email(client, email, "Cognito Identity Provider registration completed", &body).await?;
24+
}
25+
26+
// Cognito always expect a response with the same shape as
27+
// the event when it handles Post Confirmation triggers.
28+
Ok(payload)
29+
}
30+
31+
async fn send_post_confirmation_email(client: &Client, email: &str, subject: &str, body: &str) -> Result<(), Error> {
32+
let destination = Destination::builder().to_addresses(email).build();
33+
let subject = Content::builder().data(subject).build()?;
34+
let body = Content::builder().data(body).build()?;
35+
36+
let message = Message::builder()
37+
.body(Body::builder().text(body).build())
38+
.subject(subject)
39+
.build();
40+
41+
client
42+
.send_email()
43+
.source(SOURCE_EMAIL)
44+
.destination(destination)
45+
.message(message)
46+
.send()
47+
.await?;
48+
49+
Ok(())
50+
}
51+
52+
#[tokio::main]
53+
async fn main() -> Result<(), Error> {
54+
tracing::init_default_subscriber();
55+
56+
let config = aws_config::load_defaults(BehaviorVersion::latest()).await;
57+
let client = Client::new(&config);
58+
59+
run(service_fn(|event| function_handler(&client, event))).await
60+
}

lambda-events/src/event/cognito/mod.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,13 +84,18 @@ pub enum CognitoEventUserPoolsPreAuthenticationTriggerSource {
8484
/// allowing the Lambda to send custom messages or add custom logic.
8585
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
8686
#[serde(rename_all = "camelCase")]
87-
pub struct CognitoEventUserPoolsPostConfirmation {
87+
pub struct CognitoEventUserPoolsPostConfirmation<T = CognitoEventUserPoolsPostConfirmationResponse>
88+
where
89+
T: DeserializeOwned,
90+
T: Serialize,
91+
{
8892
#[serde(rename = "CognitoEventUserPoolsHeader")]
8993
#[serde(flatten)]
9094
pub cognito_event_user_pools_header:
9195
CognitoEventUserPoolsHeader<CognitoEventUserPoolsPostConfirmationTriggerSource>,
9296
pub request: CognitoEventUserPoolsPostConfirmationRequest,
93-
pub response: CognitoEventUserPoolsPostConfirmationResponse,
97+
#[serde(bound = "")]
98+
pub response: T,
9499
}
95100

96101
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize, Default)]
@@ -254,6 +259,7 @@ pub struct CognitoEventUserPoolsPostConfirmationRequest {
254259
/// `CognitoEventUserPoolsPostConfirmationResponse` contains the response portion of a PostConfirmation event
255260
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
256261
pub struct CognitoEventUserPoolsPostConfirmationResponse {}
262+
257263
/// `CognitoEventUserPoolsPreTokenGenRequest` contains request portion of PreTokenGen event
258264
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
259265
#[serde(rename_all = "camelCase")]

0 commit comments

Comments
 (0)