configuration
Auth and Secrets
Configure Better Auth, Turnstile, Resend, and Google OAuth for local and production environments.
Spinupmail's auth setup is split across Worker secrets, frontend environment variables, and a few provider dashboards. The README already covers the exact values and callback rules. This page condenses that flow into one setup guide.
Set the production Worker secrets
Run the secret commands from packages/backend and provide the real values
when Wrangler prompts.
pnpm exec wrangler secret put BETTER_AUTH_BASE_URL
pnpm exec wrangler secret put BETTER_AUTH_SECRET
pnpm exec wrangler secret put INTEGRATION_SECRET_ENCRYPTION_KEY
pnpm exec wrangler secret put CORS_ORIGIN
pnpm exec wrangler secret put RESEND_API_KEY
pnpm exec wrangler secret put TURNSTILE_SECRET_KEY
pnpm exec wrangler secret put GOOGLE_CLIENT_ID
pnpm exec wrangler secret put GOOGLE_CLIENT_SECRET| Secret | What it should contain |
|---|---|
BETTER_AUTH_BASE_URL | Your deployed auth endpoint, such as https://api.spinupmail.com/api/auth |
BETTER_AUTH_SECRET | A strong random secret, for example from openssl rand -base64 32 |
INTEGRATION_SECRET_ENCRYPTION_KEY | Base64-encoded 32-byte key used to encrypt integration provider credentials (for example Telegram bot tokens), for example from openssl rand -base64 32 |
CORS_ORIGIN | Comma-separated frontend origins, such as https://app.spinupmail.com |
EXTENSION_REDIRECT_ORIGINS | Comma-separated exact extension redirect origins, such as https://<extension-id>.chromiumapp.org |
RESEND_API_KEY | Resend API key used for verification and reset email |
TURNSTILE_SECRET_KEY | Cloudflare Turnstile secret key |
GOOGLE_CLIENT_ID | Google OAuth web client ID |
GOOGLE_CLIENT_SECRET | Google OAuth web client secret |
For integration provider setup and subscription flow, see Integrations.
Add local development secrets
For wrangler dev, create packages/backend/.dev.vars and keep it out of
version control.
BETTER_AUTH_BASE_URL="http://localhost:8787/api/auth"
BETTER_AUTH_SECRET=""
INTEGRATION_SECRET_ENCRYPTION_KEY=""
CORS_ORIGIN="http://localhost:5173,http://127.0.0.1:5173"
EXTENSION_REDIRECT_ORIGINS="https://<your-extension-id>.chromiumapp.org"
RESEND_API_KEY=""
TURNSTILE_SECRET_KEY=""
GOOGLE_CLIENT_ID=""
GOOGLE_CLIENT_SECRET=""The frontend also needs the Turnstile site key locally:
VITE_TURNSTILE_SITE_KEY=<your site key>Configure Turnstile and Resend
Turnstile protects the auth forms, while Resend delivers verification and password-reset email.
Turnstile
- Open the Cloudflare dashboard and create a Turnstile widget.
- Add the frontend hostnames that will render the widget, such as
localhost,127.0.0.1, and your production frontend domain. - Keep the widget in managed mode.
- Save the generated keys.
- Put the secret key in the Worker as
TURNSTILE_SECRET_KEY. - Put the site key in the frontend as
VITE_TURNSTILE_SITE_KEY.
Resend
- Add and verify your sending domain in Resend.
- Create an API key with permission to send email.
- Store it as the
RESEND_API_KEYWorker secret. - Set
[vars].RESEND_FROM_EMAILinpackages/backend/wrangler.tomlto a sender on that verified domain, such asSpinupmail <verify@mail.your-domain.com>.
Configure Google OAuth callbacks
Create a Google OAuth web client and use the frontend and backend URLs from your actual deployment.
Authorized JavaScript origins
http://127.0.0.1:5173http://localhost:5173https://<your-frontend-domain>.com
Authorized redirect URIs
http://localhost:8787/api/auth/callback/googlehttps://api.<your-domain>/api/auth/callback/googlehttps://<your-frontend-domain>/api/auth/callback/googleonly if your Worker is intentionally routed under/api/*on the same host
After Google issues the credentials, store them with:
pnpm exec wrangler secret put GOOGLE_CLIENT_ID
pnpm exec wrangler secret put GOOGLE_CLIENT_SECRETFor local development, also add GOOGLE_CLIENT_ID and
GOOGLE_CLIENT_SECRET to packages/backend/.dev.vars.
Optional auth domain restriction
If you set AUTH_ALLOWED_EMAIL_DOMAIN in Worker vars, Spinupmail restricts
email and Google-based auth to that domain. The Google flow also receives the
same domain as a hosted-domain hint, which keeps the auth UX aligned with
internal-only deployments.

