From efe17d2965bbe770e6d6214114081e24346b5670 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 7 Jul 2026 22:29:27 -0500 Subject: [PATCH] Modernize email: Gmail OAuth2 sending, configured under Settings Retire env-var SMTP basic-auth and send the contact form through Gmail over OAuth2 (SMTP XOAUTH2), configured in Admin -> Settings -> Email via an in-app "Connect Gmail" consent flow. Reuses the existing google SSO OAuth client; the captured refresh token is stored AES-GCM-encrypted (write-only over the API, never returned), mirroring the auth-provider and Discord-bot secret patterns. - schema: new email_config singleton table (mirrors bot_config) - model: emailConfig.{db,model} with encrypted refresh token + getSafe/getWithSecret - mailer: nodemailer OAuth2 transport (client id/secret from the google provider row), contact recipient = contact_email setting, mailto: fallback preserved, plus sendTest() - routes/controller: /admin/email config, connect start+callback (ssoState CSRF + PKCE), test, disconnect - client: EmailDelivery section on the Settings page + api methods; Settings copy now spells out that contact_email is the delivery recipient - docs/env: drop SMTP_*/CONTACT_TO from env examples; update README/BACKEND_DESIGN - tests: emailConfig.model + mailer suites (8 new; full suite 142 pass) Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01XKeCQEJZr1AFJN4Bgcmvh3 --- website/BACKEND_DESIGN.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/website/BACKEND_DESIGN.md b/website/BACKEND_DESIGN.md index 4bf9c9a..2d947be 100644 --- a/website/BACKEND_DESIGN.md +++ b/website/BACKEND_DESIGN.md @@ -234,10 +234,13 @@ who"; `activity_log` provides the history feed. ## 7. Email -`utils/mailer.js` (nodemailer) configured from `SMTP_HOST/PORT/USER/PASS`, sending to -`CONTACT_TO` (default UOMysticmoon@gmail.com). No Gmail password in code — env only. -If SMTP is unconfigured, `POST /public/contact` returns `{fallback:"mailto", email}` so the -client renders a `mailto:` link instead. Site mode changes / errors never leak SMTP creds. +`utils/mailer.js` (nodemailer) sends through **Gmail over OAuth2 (SMTP XOAUTH2)**, configured in +Admin → Settings → Email — not env. The mailbox is authorized by an in-app "Connect Gmail" consent +flow (`/admin/email/*`) that captures a refresh token, stored AES-GCM-encrypted in the `email_config` +singleton (never returned over the API). The OAuth client id/secret are reused from the `google` +auth-providers row. Recipient is the `contact_email` site setting. If email is unconfigured/disabled, +`POST /public/contact` returns `{fallback:"mailto", email}` so the client renders a `mailto:` link +instead. Errors never leak credentials. --- @@ -287,11 +290,7 @@ COOKIE_SECURE=true COOKIE_NAME=uomm_token ADMIN_USERNAME= ADMIN_PASSWORD= -SMTP_HOST= -SMTP_PORT=587 -SMTP_USER= -SMTP_PASS= -CONTACT_TO=UOMysticmoon@gmail.com +# Email: configured in Admin → Settings → Email (Gmail OAuth2), not via env CLIENT_ORIGIN=http://localhost:5173 ```