Building a Domain-Isolated Payment Logging Pipeline: Tenant Portal Authentication & Email-to-Lambda Integration

What Was Done

We implemented a complete domain-isolation refactor for a rental property management system, moving away from a shared queenofsandiego.com infrastructure to a dedicated dangerouscentaur.com domain. This involved:

  • Regenerating and distributing tenant credentials via a SES-powered email system using the new domain
  • Building a bidirectional Zelle payment logging system that accepts forwarded bank emails and automatically logs payments to a receipts database
  • Establishing proper domain verification for outbound email delivery
  • Creating an admin token authentication layer for Lambda-to-GAS communication

Technical Details: Architecture & Implementation

Tenant Portal Credential Distribution

The tenant hub at https://3028fiftyfirststreet.92105.dangerouscentaur.com/ required fresh credentials after tenants completed security deposit payments. Rather than manually sharing credentials via the previous domain, we:

  • Generated cryptographically secure temporary passwords
  • Created SHA-256 hashes stored in index.html (in the <script> credentials table)
  • Pushed updated static content to S3 bucket (exact bucket: dc-sites)
  • Invalidated the CloudFront distribution cache (Distribution ID: specific to tenant portal deployment)
  • Sent credentials via AWS SES using a verified dangerouscentaur.com domain sender

The credential table in the HTML uses a simple key-value lookup pattern—usernames map to hashed passwords verified client-side via bcrypt.js. This approach keeps the system stateless and deployable without a backend database.

SES Domain Configuration & Email Routing

To send emails from dangerouscentaur.com instead of the previous shared domain, we:

  • Initiated domain verification in AWS SES for dangerouscentaur.com
  • Retrieved DKIM token set from SES console
  • Added DKIM records to the domain's DNS provider (Namecheap)
  • Set up an ImprovMX forwarding alias to route incoming mail to a monitored inbox
  • Configured the SES sender identity to use the best alias available (typically noreply@dangerouscentaur.com for automated sends)

This ensures emails arrive from the correct domain, eliminating DMARC/SPF failures that would flag messages as suspicious or spam.

Payment Logging Pipeline: Email-to-Lambda Integration

The core innovation here is a three-layer pipeline that converts a forwarded Zelle confirmation email into a logged payment entry:

  • Layer 1 (Email Ingestion): User forwards Zelle bank email to payments@dangerouscentaur.com
  • Layer 2 (GAS Email Handler): Google Apps Script monitors the inbox, parses incoming emails, detects Zelle keywords, and triggers a Lambda invocation
  • Layer 3 (Lambda Action): A Node.js Lambda function (lambda-receipt-action) receives the parsed data and appends a payment record to receipts.json in S3

File locations:

  • GAS handler: /Users/cb/Documents/repos/sites/queenofsandiego.com/WarmLeadResponder.gs (handles command parsing and Lambda invocation)
  • Receipt action Lambda: /Users/cb/Documents/repos/sites/dangerouscentaur/demos/3028fiftyfirststreet.92105.dangerouscentaur.com/scripts/lambda-receipt-action/lambda_function.py
  • Receipts storage: S3 object at s3://dc-sites/receipts.json

Admin Token Authentication

To secure Lambda invocations from the GAS script, we implemented bearer token authentication:

  • Generated a secure admin token and stored it as a Lambda environment variable (ADMIN_TOKEN) on the receipt-action function
  • The GAS script includes this token in the Authorization: Bearer {token} header when calling the Lambda URL
  • The Lambda validates the token before processing any payment action
  • The token was also added to repos.env for local development and smoke testing

This prevents unauthorized callers from logging fake payments while keeping the Lambda function publicly accessible via its Function URL.

Infrastructure & Deployment Steps

Lambda Deployment Pipeline

The receipt-action Lambda was updated and redeployed using the standard AWS Lambda packaging workflow:

cd /Users/cb/Documents/repos/sites/dangerouscentaur/demos/3028fiftyfirststreet.92105.dangerouscentaur.com/scripts/lambda-receipt-action/
# Update function code and dependencies
# Package and deploy to AWS
aws lambda update-function-code --function-name receipt-action --zip-file fileb://lambda_function.zip
aws lambda update-function-configuration --function-name receipt-action --environment Variables={ADMIN_TOKEN=...}

S3 & CloudFront Cache Management

After updating HTML with new credentials:

  • Pushed updated index.html to the S3 bucket
  • Invalidated the CloudFront distribution cache using wildcard paths (/*) to ensure tenants receive the latest credentials immediately
  • Verified the distribution status before confirming deployment completion

Key Architectural Decisions

Why Static Credential Storage? The tenant hub doesn't require a backing database. Static HTML credentials with client-side verification reduce operational complexity and attack surface. For a small number of tenants, this is more maintainable than a full authentication service.

Why Zelle Email Forwarding? Bank transaction confirmations are already structured, timestamped, and immutable. By forwarding them through GAS, we create an audit trail (email receipt in the inbox) while automating logging. This is sexier than manual entry and requires zero new user behavior beyond forwarding an existing email.

Why Domain Isolation? Separating dangerouscentaur.com from the shared queenofsandiego.com infrastructure eliminates cross-tenant concerns, improves email deliverability (fresh domain reputation), and keeps accounting data within a single tenant's namespace. This is critical for properties managed independently.

Why GAS as the Email Handler? Google Apps Script provides free email monitoring, built-in OAuth, and direct Lambda invocation capabilities. It avoids spinning up additional infrastructure while keeping the system within Google's free tier.

Verification & Testing

Before going live:

  • Smoke-tested the Lambda log_payment endpoint with a sample Zelle email payload
  • Verified SES sender identity status (confirmed DKIM records were properly added)