Skip to content

Move custom email verification to app-level (1/3)#2679

Merged
drew-harris merged 21 commits into
mainfrom
drewh/validate-senders-app-level
Jun 1, 2026
Merged

Move custom email verification to app-level (1/3)#2679
drew-harris merged 21 commits into
mainfrom
drewh/validate-senders-app-level

Conversation

@drew-harris
Copy link
Copy Markdown
Contributor

@drew-harris drew-harris commented May 15, 2026

This PR migrates the current user-based system for custom oauth email senders to use app ids instead. This adds another on top of the postmark email to verify sender. We will also send a magic code to the user so that they can link the sender's identity with the app.

This is gated behind the boolean feature flag: use-app-email-verification?

Added Routes
POST /dash/apps/:id/sender-verification/send-magic-code
POST /dash/apps/:id/sender-verification/verify-magic-code

I also updated the frontend to include the ui needed for seeing the status of both verification steps and submitting the magic code / resending. It is backwards compatible with the backend feature flag.

Important! Includes database migrations which must be run before turning on the feature flag.

Deploy Plan

  1. merge pr
  2. run migrations included in pr
  3. deploy backend
  4. toggle use-app-email-verification? feature flag to true

How to test:
image
Turn on the feature flag.

Try adding / changing an oauth email template with a custom sender address on the dashboard.

image

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 15, 2026

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • ✅ Review completed - (🔄 Check again to review again)
📝 Walkthrough

Walkthrough

Adds DB persistence and model for sender verification codes, server endpoints (send/verify) with rate limiting and feature-flag gating, integrates verification into magic-code send logic, and provides a client hook and UI to send/resend and verify 6‑digit OTPs.

Changes

Custom Sender Email Verification with OTP

Layer / File(s) Summary
Database schema and verification code persistence
server/resources/migrations/111_add_app_email_verification_codes.{up,down}.sql, server/src/instant/model/app_email_verification_code.clj, server/src/instant/model/app_email_verification.clj
Creates app_email_verification_codes table with indexes; adds model functions put!, consume!, and format-email. Updates app_email_verification.put! to upsert on (app_id, sender_id) and return id and verified.
Verification endpoints and route wiring
server/src/instant/dash/routes.clj
Adds sender-verification-send-magic-code and sender-verification-verify-magic-code handlers with feature-flag gating and rate limiting, creates/consumes codes in transactions, updates sender-verification-get to include :instant verified flag and Postmark details, persists sender_id on email-template POST, and registers POST routes for send/verify.
Rate limiting and magic-code integration
server/src/instant/rate_limit.clj, server/src/instant/runtime/magic_code_auth.clj
Adds deterministic per-email custom-sender rate-limit helper and check-custom-sender-rate-limit!; send! consults verification state and uses custom sender only when verified.
Feature flag and sender model integration
server/src/instant/flags.clj, server/src/instant/model/app_email_sender.clj, server/src/instant/model/app_email_template.clj
Adds use-app-email-verification? flag (default false). sync-sender! conditionally initializes verification rows and now returns {:sender sender}; templates now include sender_id and verified selection.
Client-side verification state hook
client/www/lib/hooks/useCustomSenderVerification.ts
New hook using SWR to fetch verification, endpoints for sending/verifying magic-code emails, normalizes 6-digit codes, exposes instantVerified/postmarkVerified, sendCode(), verifyCode(), refetch(), loading, and justSentCode.
Client UI integration and OTP component
client/www/components/dash/auth/Email.tsx
Replaces local verification scaffolding with useCustomSenderVerification(app). On save and initial load it refetches verification and may auto-send an OTP. Updates Postmark/DKIM/Return-Path UI to use hook data and adds SenderOtpVerification for 6‑digit input/send/verify.
Integration and model tests
server/test/instant/*
Adds tests for verification code consumption and end-to-end magic-code send/verify flows; updates test requires and helpers for feature-flagged custom-sender scenarios.
Runtime routes HTML reflow (formatting)
server/src/instant/runtime/routes.clj
Reflowed/renumbered Hiccup HTML in OAuth callback testing landing handlers without semantic changes.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant SendHandler as sender-verification-send-magic-code
  participant RateLimit as rate_limit.try-custom-sender
  participant CodeModel as app_email_verification_code.put!
  participant DB as app_email_verification_codes
  participant VerifyHandler as sender-verification-verify-magic-code
  Client->>SendHandler: POST /dash/apps/:app_id/sender-verification/send (email)
  SendHandler->>RateLimit: try-custom-sender(email)
  SendHandler->>CodeModel: put!(code, app_id, verification_id)
  CodeModel->>DB: INSERT code row
  SendHandler->>Client: send formatted email (format-email)
  Client->>VerifyHandler: POST /dash/apps/:app_id/sender-verification/verify (code)
  VerifyHandler->>CodeModel: consume!(code, verification_id, expiry)
  CodeModel->>DB: DELETE returned row if within expiry
  VerifyHandler->>DB: mark verification verified (transaction)
  VerifyHandler->>Client: {:verified true} or validation error
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • dwwoelfel
  • stopachka
  • nezaj
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description check ✅ Passed The description comprehensively explains the migration from user-based to app-level custom email verification, the feature flag, new routes, frontend updates, database migration requirements, and includes deployment and testing instructions.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title 'Move custom email verification to app-level (1/3)' directly and accurately describes the primary goal of this changeset—migrating from user-based to app-level verification. It is concise, specific, and clearly conveys the main change.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch drewh/validate-senders-app-level

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@drew-harris drew-harris changed the title Move custom email verification to app-level Move custom email verification to app-level (1/2) May 15, 2026
@drew-harris drew-harris force-pushed the drewh/validate-senders-app-level branch from 253b606 to 19eff43 Compare May 15, 2026 21:30
@drew-harris drew-harris force-pushed the drewh/validate-senders-app-level branch from 19eff43 to 68c7afb Compare May 15, 2026 21:30
@github-actions
Copy link
Copy Markdown
Contributor

Comment thread server/src/instant/app_email_verification.clj Outdated
Comment thread server/src/instant/model/app_email_verification.clj Outdated
Comment thread server/src/instant/model/app_email_verification.clj Outdated
Comment thread server/src/instant/model/app_email_verification.clj Outdated
Comment thread server/resources/migrations/111_add_app_email_verification_codes.up.sql Outdated
Comment thread server/src/instant/dash/routes.clj Outdated
@drew-harris drew-harris force-pushed the drewh/validate-senders-app-level branch 2 times, most recently from f48d7f6 to 115db8a Compare May 18, 2026 18:01
Copy link
Copy Markdown
Contributor

@stopachka stopachka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall LGTM!

One higher level issue I had was with the naming.

Frontend

  • The world verification is stacked with the word verify.
    • verifySenderVerificationCode
  • The other names are long and slightly different, which makes it hard to understand the data model
  • For example, consider getSenderVerification and sendSenderVerificationCode
    • I guess SenderVerification is the object, while SenderVerificationCode is just the code linked to the object. But this can be hard to parse out

Suggestion

Maybe we cross out the word verification from the codes:

  • getSenderVerification stays
  • The rest are sendSenderMagicCode and verifySenderMagicCode

HTTP

  • The HTTP endpoint has the same issue of overloading verify with verification.

Suggestion

Maybe we follow the same idea:

  GET    /dash/apps/:app_id/sender-verification
  POST   /dash/apps/:app_id/sender-verification/send-magic-code
  POST   /dash/apps/:app_id/sender-verification/verify-magic-code

This drops some of the pretense on REST, which imo is fine for the sake of clarity.

You can then change the handler names too, to fns like sender-verification-send-magic-code etc

The response structure is also confusing:

  • sender-verification is a boolean, when the expectation would be an object

Suggestion

Maybe we return a response like:

:sender { email }
:postmark { id, ... }
:instant { 'verified?' }

verification
.sendCode()
.then(() => {
successToast(`Verification code sent to ${address}`);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I never see this success toast when I click the (re)send code button.

Copy link
Copy Markdown
Contributor

@dwwoelfel dwwoelfel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, one comment about the success toast not showing up for me.

@drew-harris drew-harris force-pushed the drewh/validate-senders-app-level branch from 112b5c1 to ac7068e Compare May 27, 2026 17:09
@drew-harris drew-harris force-pushed the drewh/validate-senders-app-level branch from ac7068e to d4332ea Compare May 27, 2026 21:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants