|
1 | 1 | # Cognito Custom Message Sender |
2 | 2 |
|
3 | | -This AWS Lambda-based solution enables dynamic, policy-driven email responses to |
4 | | -AWS Cognito events, utilizing AWS Simple Email Service (SES) for delivery. |
5 | | -Tailor email content and sending behavior dynamically with Open Policy Agent |
6 | | -(OPA) policies, providing a flexible and powerful tool for managing user |
7 | | -communications in response to specific triggers within AWS Cognito. Ideal for |
8 | | -applications requiring customized user engagement or notification strategies. |
| 3 | +A flexible AWS Lambda–based solution to send policy-driven emails in response |
| 4 | +to AWS Cognito events. It supports SES for delivery, optional SendGrid email |
| 5 | +verification, and a local debug mode for integration testing without real |
| 6 | +credentials or addresses. |
9 | 7 |
|
10 | 8 | ## Features |
11 | 9 |
|
12 | | -- **AWS Lambda Integration**: Handle Cognito Custom Email Sender events, sending |
13 | | - emails according to OPA policies. |
14 | | -- **Policy-based Email Sending**: Use OPA for fine-grained control over email |
15 | | - content and sending behavior. |
| 10 | +* **AWS Lambda Integration**: Handle Cognito Custom Email Sender events and |
| 11 | + deliver emails based on OPA policies. |
| 12 | +* **Policy-based Email Sending**: Evaluate Rego policies to allow or deny |
| 13 | + sending, and to customize template ID, data, source, and destination |
| 14 | + addresses. |
| 15 | +* **SendGrid Verification (optional)**: Fetch and include SendGrid email |
| 16 | + verification data as policy input (disabled by default). |
| 17 | +* **Local Debug Mode**: Run integration tests against example event data and |
| 18 | + policies, with mocked KMS decryption and dry-run sending. |
| 19 | +* **Dry-run Support**: Log SES requests instead of sending when |
| 20 | + `APP_SEND_ENABLED=false` or in debug mode. |
| 21 | + |
| 22 | +## Environment Variables |
| 23 | + |
| 24 | +Configure your Lambda or local environment via environment variables: |
| 25 | + |
| 26 | +| Variable | Description | Default | |
| 27 | +| ------------------------------------- | ------------------------------------------------------------------------- | --------------------------------- | |
| 28 | +| `APP_DEBUG_MODE` | `true` to enable debug mode (loads `.env`, mocks KMS and dry-run). | `false` | |
| 29 | +| `APP_DEBUG_DATA_PATH` | Path to JSON file containing array of Cognito event samples (for debug). | `fixtures/debug-data.json` | |
| 30 | +| `APP_EMAIL_SENDER_POLICY_PATH` | Path or S3 URI to the Rego policy file used by OPA. | **required** | |
| 31 | +| `APP_KMS_KEY_ID` | KMS key ID for decrypting the Cognito code. | **required** | |
| 32 | +| `APP_LOG_LEVEL` | Log level (`debug`, `info`, `warn`, `error`). | `info` | |
| 33 | +| `APP_SEND_ENABLED` | `true` to send via SES, `false` to dry-run. | `true` | |
| 34 | +| `SENDGRID_API_KEY` | SendGrid API key for email verification. | **required if enabling SendGrid** | |
| 35 | +| `SENDGRID_API_HOST` | Base URL for SendGrid API. | `https://api.sendgrid.com` | |
| 36 | +| `SENDGRID_EMAIL_VERIFICATION_ENABLED` | `true` to include SendGrid verification in policy input, `false` to skip. | `false` | |
| 37 | + |
| 38 | +> **Note:** `APP_SEND_ENABLED` is automatically set to `false` in debug mode unless explicitly overridden. |
| 39 | +
|
| 40 | +## SendGrid Email Verification (Optional) |
| 41 | + |
| 42 | +SendGrid's email address verification API improves the security and reliability |
| 43 | +of your Cognito workflows by proactively detecting invalid or risky email |
| 44 | +addresses before attempting to send. This helps reduce bounce rates, avoid AWS |
| 45 | +SES suppression, and prevent abuse by filtering out disposable, mistyped, or |
| 46 | +role-based emails. |
| 47 | + |
| 48 | +To include SendGrid verification results as input to your OPA policy: |
| 49 | + |
| 50 | +1. Set `SENDGRID_EMAIL_VERIFICATION_ENABLED=true`. |
| 51 | +2. Provide `SENDGRID_API_KEY` via environment. Optionally override host with |
| 52 | + `SENDGRID_API_HOST`. |
| 53 | +3. In your Rego policy, reference `input.emailVerification` fields (`valid`, |
| 54 | + `score`, `role`, `raw`). |
| 55 | + |
| 56 | +Example policy snippet: |
| 57 | + |
| 58 | +```rego |
| 59 | +package cognito_custom_sender_email_policy |
| 60 | +
|
| 61 | +result := deny_result { |
| 62 | + input.emailVerification != null |
| 63 | + not input.emailVerification.valid |
| 64 | +} |
| 65 | +
|
| 66 | +result := allow_result { |
| 67 | + not deny_result |
| 68 | +} |
| 69 | +
|
| 70 | +allow_result := { |
| 71 | + "action": "allow", |
| 72 | + "allow": { |
| 73 | + "templateID": "verification-template", |
| 74 | + "templateData": {}, |
| 75 | + "srcAddress": "noreply@example.com", |
| 76 | + "dstAddress": input.userAttributes.email |
| 77 | + } |
| 78 | +} |
| 79 | +
|
| 80 | +deny_result := { |
| 81 | + "action": "deny", |
| 82 | + "reason": "email verification failed" |
| 83 | +} |
| 84 | +``` |
| 85 | + |
| 86 | +## OPA Policy Input |
| 87 | + |
| 88 | +The Rego policy receives a single `input` object with the following shape: |
| 89 | + |
| 90 | +```jsonc |
| 91 | +{ |
| 92 | + "trigger": "CustomEmailSender_SignUp", |
| 93 | + "userAttributes": { |
| 94 | + "email": "user@example.org", |
| 95 | + "email_verified": "false", |
| 96 | + "sub": "uuid" |
| 97 | + }, |
| 98 | + "clientMetadata": { |
| 99 | + "key": "value" |
| 100 | + }, |
| 101 | + // only available if sendgrid is enabled |
| 102 | + "emailVerification": { |
| 103 | + "valid": true, |
| 104 | + "score": 0.97, |
| 105 | + "raw": "{...raw sendgrid response...}" |
| 106 | + } |
| 107 | +} |
| 108 | +``` |
| 109 | + |
| 110 | +> `emailVerification` is omitted if SendGrid verification is disabled. |
| 111 | +
|
| 112 | +The Rego policy must return a single object at `data.cognito_custom_sender_email_policy.result` |
| 113 | +with the following shape: |
| 114 | + |
| 115 | +* Deny example: |
| 116 | + |
| 117 | +```json |
| 118 | +{ |
| 119 | + "action": "deny", |
| 120 | + "reason": "email verification failed" |
| 121 | +} |
| 122 | +``` |
| 123 | + |
| 124 | +* Allow example: |
| 125 | + |
| 126 | +```json |
| 127 | +{ |
| 128 | + "action": "allow", |
| 129 | + "allow": { |
| 130 | + "templateID": "your-ses-template-id", |
| 131 | + "templateData": { |
| 132 | + "code": "123456" |
| 133 | + }, |
| 134 | + "srcAddress": "noreply@example.org", |
| 135 | + "dstAddress": "user@example.org" |
| 136 | + } |
| 137 | +} |
| 138 | +``` |
| 139 | + |
| 140 | +## Debug Mode & Local Integration Tests |
| 141 | + |
| 142 | +Use the `cmd/debug` utility to run against local fixtures without real emails or |
| 143 | +KMS: |
| 144 | + |
| 145 | +1. Copy or modify `.env.example` to `.env` and adjust values. |
| 146 | +2. Build the debug binary: |
| 147 | + |
| 148 | + ```bash |
| 149 | + go run -C cmd/debug . |
| 150 | + ``` |
| 151 | + |
| 152 | +3. Override fixtures: |
| 153 | + |
| 154 | + ```bash |
| 155 | + go run -C cmd/debug -data path/to/events.json -policy path/to/policy.rego |
| 156 | + ``` |
| 157 | + |
| 158 | +This mode: |
| 159 | + |
| 160 | +* Loads environment variables from `.env`. |
| 161 | +* Mocks KMS decryption if `APP_KMS_KEY_ID=MOCKED_KEY_ID`. |
| 162 | +* Dry-runs SES requests. |
| 163 | + - Set `APP_SEND_ENABLED=true` to explicitly enable SES sends |
| 164 | + |
| 165 | +## Build & Deployment |
| 166 | + |
| 167 | +1. Clone the repo: |
| 168 | + |
| 169 | + ```bash |
| 170 | + git clone https://github.com/cruxstack/cognito-custom-message-sender-go.git |
| 171 | + cd cognito-custom-message-sender-go |
| 172 | + ``` |
| 173 | +2. Build for Linux: |
| 174 | + |
| 175 | + ```bash |
| 176 | + GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o bootstrap main.go |
| 177 | + ``` |
| 178 | +3. Package: |
| 179 | + |
| 180 | + ```bash |
| 181 | + zip deployment.zip bootstrap policy.rego |
| 182 | + ``` |
| 183 | +4. Deploy via AWS CLI, Terraform, or AWS Console as an `al2023provided.al2023` |
| 184 | + runtime, setting the required environment variables and IAM role with SES, |
| 185 | + KMS, and OPA policy access. |
16 | 186 |
|
17 | | -## Lambda Function |
18 | | - |
19 | | -Trigger the Lambda function by configuring Cognito to send Custom Email Sender |
20 | | -events. Ensure your Lambda function is set as the destination for these events. |
21 | | - |
22 | | -### Build |
23 | | - |
24 | | -#### Prerequisites |
25 | | - |
26 | | -- AWS account with access to SES, Lambda, and KMS. |
27 | | -- Configured AWS CLI with appropriate permissions. |
28 | | -- Go 1.22.1+ installed for building the project. |
29 | | -- Knowledge of Open Policy Agent for defining policies. |
30 | | - |
31 | | -#### Steps |
32 | | - |
33 | | -1. Clone the repository: |
34 | | - |
35 | | - ```bash |
36 | | - git clone https://github.com/cruxstack/cognito-custom-message-sender-go.git |
37 | | - cd cognito-custom-message-sender-go |
38 | | - ``` |
39 | | - |
40 | | -2. Build the project for Linux as `bootstrap` binary: |
41 | | - |
42 | | - ```bash |
43 | | - GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o bootstrap |
44 | | - ``` |
45 | | - |
46 | | -3. Add OPA Policy: |
47 | | - |
48 | | - ```rego |
49 | | - package cognito_custom_sender_email_policy |
50 | | - result := { |
51 | | - "action": "allow", |
52 | | - "allow": { |
53 | | - "templateID": "REPLACE_WITH_SES_TEMPLATE_NAME", |
54 | | - "templateData": {}, |
55 | | - "srcAddress": "noreply@example.com", |
56 | | - "dstAddress": input.userAttributes.email, |
57 | | - }, |
58 | | - } |
59 | | - ``` |
60 | | - |
61 | | -4. Create a ZIP archive: |
62 | | - |
63 | | - ```bash |
64 | | - zip deployment.zip main policy.rego |
65 | | -
|
66 | | - ``` |
67 | | - |
68 | | -### Create Lambda Function |
69 | | - |
70 | | -#### Steps |
71 | | - |
72 | | -- Create a KMS key in the AWS Management Console |
73 | | - - Required as AWS uses it to encrypt the verification code. |
74 | | -- Create a IAM Role with the following permissions: |
75 | | - - `AWSLambdaBasicExecutionRole` Managed Policy |
76 | | - - `kms:Decrypt` for the KMS key |
77 | | - - `ses:GetTemplate` for fetching SES templates |
78 | | - - `ses:SendTemplatedEmail` for sending emails |
79 | | -- Create a Lambda function in the AWS Management Console |
80 | | - - Runtime: `al2023provided.al2023` |
81 | | - - Handler: `bootstrap` |
82 | | - - IAM Role: Use the IAM Role created earlier |
83 | | - - Environment Variables: |
84 | | - - `KMS_KEY_ID`: KMS key ID for decrypting OPA policy |
85 | | - - `POLICY_PATH`: S3 path to OPA policy |
86 | | - - Code: Upload the ZIP archive |
87 | | - - Permissions: Allow Cognito to invoke the Lambda function |
88 | | -- Configure Cognito to send Custom Email Sender events to the Lambda function |
89 | | - - Configure the Cognito to use the same KMS key for encryption |
|
0 commit comments