Skip to content

Commit

Permalink
Add Cognito Post Confirmation example (#884)
Browse files Browse the repository at this point in the history
- 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 <david.calavera@gmail.com>
  • Loading branch information
calavera committed May 31, 2024
1 parent 00d822e commit 31250f3
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 2 deletions.
1 change: 1 addition & 0 deletions examples/basic-cognito-post-confirmation/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/target
24 changes: 24 additions & 0 deletions examples/basic-cognito-post-confirmation/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[package]
name = "basic-cognito-post-confirmation"
version = "0.1.0"
edition = "2021"

# Starting in Rust 1.62 you can use `cargo add` to add dependencies
# to your project.
#
# If you're using an older Rust version,
# download cargo-edit(https://github.com/killercup/cargo-edit#installation)
# to install the `add` subcommand.
#
# Running `cargo add DEPENDENCY_NAME` will
# add the latest version of a dependency to the list,
# and it will keep the alphabetic ordering for you.

[dependencies]
aws-config = "1.5.0"
aws-sdk-ses = "1.28.0"
aws_lambda_events = { path = "../../lambda-events", default-features = false, features = ["cognito"] }

lambda_runtime = { path = "../../lambda-runtime" }
tokio = { version = "1", features = ["macros"] }

15 changes: 15 additions & 0 deletions examples/basic-cognito-post-confirmation/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Cognito Post Confirmation Request example

This example shows how to write a Lambda function in Rust to process Cognito's Post Confirmation requests.

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

## Build & Deploy

1. Install [cargo-lambda](https://github.com/cargo-lambda/cargo-lambda#installation)
2. Build the function with `cargo lambda build --release`
3. Deploy the function to AWS Lambda with `cargo lambda deploy`

## Build for ARM 64

Build the function with `cargo lambda build --release --arm64`
60 changes: 60 additions & 0 deletions examples/basic-cognito-post-confirmation/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use aws_config::BehaviorVersion;
use aws_lambda_events::event::cognito::CognitoEventUserPoolsPostConfirmation;
use aws_sdk_ses::{
types::{Body, Content, Destination, Message},
Client,
};
use lambda_runtime::{run, service_fn, tracing, Error, LambdaEvent};

const SOURCE_EMAIL: &str = "<source_email>";

async fn function_handler(
client: &aws_sdk_ses::Client,
event: LambdaEvent<CognitoEventUserPoolsPostConfirmation>,
) -> Result<CognitoEventUserPoolsPostConfirmation, Error> {
let payload = event.payload;

if let Some(email) = payload.request.user_attributes.get("email") {
let body = if let Some(name) = payload.request.user_attributes.get("name") {
format!("Welcome {name}, you have been confirmed.")
} else {
"Welcome, you have been confirmed.".to_string()
};
send_post_confirmation_email(client, email, "Cognito Identity Provider registration completed", &body).await?;
}

// Cognito always expect a response with the same shape as
// the event when it handles Post Confirmation triggers.
Ok(payload)
}

async fn send_post_confirmation_email(client: &Client, email: &str, subject: &str, body: &str) -> Result<(), Error> {
let destination = Destination::builder().to_addresses(email).build();
let subject = Content::builder().data(subject).build()?;
let body = Content::builder().data(body).build()?;

let message = Message::builder()
.body(Body::builder().text(body).build())
.subject(subject)
.build();

client
.send_email()
.source(SOURCE_EMAIL)
.destination(destination)
.message(message)
.send()
.await?;

Ok(())
}

#[tokio::main]
async fn main() -> Result<(), Error> {
tracing::init_default_subscriber();

let config = aws_config::load_defaults(BehaviorVersion::latest()).await;
let client = Client::new(&config);

run(service_fn(|event| function_handler(&client, event))).await
}
10 changes: 8 additions & 2 deletions lambda-events/src/event/cognito/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,18 @@ pub enum CognitoEventUserPoolsPreAuthenticationTriggerSource {
/// allowing the Lambda to send custom messages or add custom logic.
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CognitoEventUserPoolsPostConfirmation {
pub struct CognitoEventUserPoolsPostConfirmation<T = CognitoEventUserPoolsPostConfirmationResponse>
where
T: DeserializeOwned,
T: Serialize,
{
#[serde(rename = "CognitoEventUserPoolsHeader")]
#[serde(flatten)]
pub cognito_event_user_pools_header:
CognitoEventUserPoolsHeader<CognitoEventUserPoolsPostConfirmationTriggerSource>,
pub request: CognitoEventUserPoolsPostConfirmationRequest,
pub response: CognitoEventUserPoolsPostConfirmationResponse,
#[serde(bound = "")]
pub response: T,
}

#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize, Default)]
Expand Down Expand Up @@ -254,6 +259,7 @@ pub struct CognitoEventUserPoolsPostConfirmationRequest {
/// `CognitoEventUserPoolsPostConfirmationResponse` contains the response portion of a PostConfirmation event
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
pub struct CognitoEventUserPoolsPostConfirmationResponse {}

/// `CognitoEventUserPoolsPreTokenGenRequest` contains request portion of PreTokenGen event
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
Expand Down

0 comments on commit 31250f3

Please sign in to comment.