Skip to content

Commit fa05e47

Browse files
authored
Merge pull request #2 from cruxstack/dev
feat: integrate sendgrid email verification api
2 parents e1dfaa8 + 2a37257 commit fa05e47

File tree

21 files changed

+704
-164
lines changed

21 files changed

+704
-164
lines changed

.editorconfig

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
[*]
2+
indent_style = space
3+
indent_size = 2
4+
end_of_line = lf
5+
insert_final_newline = true
6+
trim_trailing_whitespace = true
7+
charset = utf-8
8+
9+
[{Dockerfile,Dockerfile.*}]
10+
indent_size = 4
11+
tab_width = 4
12+
13+
[{Makefile,makefile,GNUmakefile}]
14+
indent_style = tab
15+
indent_size = 4
16+
17+
[Makefile.*]
18+
indent_style = tab
19+
indent_size = 4
20+
21+
[**/*.{go,mod,sum}]
22+
indent_style = tab
23+
indent_size = unset
24+
25+
[**/*.py]
26+
indent_size = 4

.env.example

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
APP_DEBUG_MODE=true
2+
APP_LOG_LEVEL=debug
3+
APP_SEND_ENABLED=false
4+
APP_KMS_KEY_ID=MOCKED_KEY_ID
5+
APP_SENDGRID_API_KEY=you-api-key-here
6+
APP_SENDGRID_EMAIL_VERIFICATION_ENABLED=false

.gitignore

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1-
tmp
1+
!**/.gitkeep
2+
3+
tmp/
24
.DS_Store
3-
Archive*.zip
5+
6+
.local/
7+
.env
8+
9+
cognito-custom-message-sender-go
410
main
511
cli
6-
.integration
7-
.env
12+

README.md

Lines changed: 180 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,186 @@
11
# Cognito Custom Message Sender
22

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.
97

108
## Features
119

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.
16186

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

Comments
 (0)