diff --git a/.agents/skills/authentication/SKILL.md b/.agents/skills/authentication/SKILL.md index e3eb08fea3b..959615461a5 100644 --- a/.agents/skills/authentication/SKILL.md +++ b/.agents/skills/authentication/SKILL.md @@ -23,6 +23,7 @@ Auth is powered by **Better Auth** with account-first design. Every new user cre | **Production (default)** | Magic-link-first Better Auth when outbound email is ready, with email/password fallback and social providers (Google, GitHub). Organizations built in. | | **`AUTH_MODE=local`** | **Not** a browser auth bypass, and never returns `local@localhost`. It only affects CLI/agent identity: it lets `pnpm action` / the local agent loop auto-bind to the single real signed-in dev user from the `sessions` table (see `scripts/dev-session.ts`). Browser login is unchanged. | | **`AUTH_SKIP_EMAIL_VERIFICATION=1`** | QA/preview escape hatch for password-fallback accounts. Signup skips email verification and does not send the signup verification email. Local dev/test skips verification by default; set `AUTH_SKIP_EMAIL_VERIFICATION=0` only when testing verification itself. It does not change magic-link delivery. Use `+qa` emails for test accounts. | +| **`auth.requireEmailVerification`** | Declares the password-signup verification policy for any environment, production included, via `defineAppConfig({ auth: { requireEmailVerification: false } })` or `AUTH_REQUIRE_EMAIL_VERIFICATION=0`. A declared value outranks `AUTH_SKIP_EMAIL_VERIFICATION` and the per-environment default. `false` accepts an unverified address as a login credential; `true` with no email provider disables password signup instead of stranding accounts on a verification nobody can deliver. | | **`AUTH_MAGIC_LINK=0`** | Force the email/password fallback even when outbound email is ready. | | **`AUTH_DISABLED=true`** | Skip login/signup entirely — every request runs as `dev@local.test`. For local dev, cloud previews, and internal demos only; not for production with real users. | | **`ACCESS_TOKEN` / `ACCESS_TOKENS`** | Static bearer fallback for MCP/connect clients that cannot use OAuth. Not browser auth and never a token login page. | diff --git a/.changeset/auth-require-email-verification-config.md b/.changeset/auth-require-email-verification-config.md new file mode 100644 index 00000000000..15707f6cc02 --- /dev/null +++ b/.changeset/auth-require-email-verification-config.md @@ -0,0 +1,13 @@ +--- +"@agent-native/core": minor +--- + +Add `auth.requireEmailVerification` to the app config schema, aliased to +`AUTH_REQUIRE_EMAIL_VERIFICATION`, so a deployment can state its password-signup +verification policy instead of inheriting the environment-derived one. +`AUTH_SKIP_EMAIL_VERIFICATION` stays a local/QA-only convenience that hosted +deployments ignore; a declared value outranks it. Setting the field to `false` +accepts an unverified address as a login credential and therefore also lifts the +hosted no-email-provider signup lock, which exists to prevent exactly that; +setting it to `true` where no email provider is configured disables password +signup rather than stranding accounts on a verification that cannot be delivered. diff --git a/.changeset/quiet-vite-rollup-options-alias.md b/.changeset/quiet-vite-rollup-options-alias.md new file mode 100644 index 00000000000..4740a1b3a2b --- /dev/null +++ b/.changeset/quiet-vite-rollup-options-alias.md @@ -0,0 +1,5 @@ +--- +"@agent-native/core": patch +--- + +Stop the dev server from warning that the `agent-native-config` plugin set both `rollupOptions` and `rolldownOptions`. Vite 8 exposes `rollupOptions` as a getter alias of `rolldownOptions`, and spreading the incoming `build` / `optimizeDeps` sections copied that alias back out alongside our own `rolldownOptions`. diff --git a/docs/environment-variables.md b/docs/environment-variables.md index 4106858b4c3..29e5a7b5f56 100644 --- a/docs/environment-variables.md +++ b/docs/environment-variables.md @@ -376,6 +376,7 @@ only in code. | `app.packageName` | `npm_package_name` | string | — | Package name of the running app, as npm sets it for a script. | | `app.template` | `VITE_AGENT_NATIVE_TEMPLATE` | string | — | First-party template this app was generated from. | | `auth.disableDesktopSsoFallbackInDevelopment` | `AGENT_NATIVE_DISABLE_DESKTOP_SSO_FALLBACK` | boolean | `false` | Disable the loopback Desktop SSO fallback in development so isolated acceptance runs can use their configured local identity. Ignored in production. | +| `auth.requireEmailVerification` | `AUTH_REQUIRE_EMAIL_VERIFICATION` | boolean | — | Whether password signup must verify the email address before it gets a session. Unset derives it: hosted deployments require it, local development skips it. Setting it false accepts an unverified email as a login credential, including in production. | | `integrations.allowUnverifiedWebhooks` | `AGENT_NATIVE_ALLOW_UNVERIFIED_WEBHOOKS` | boolean | `false` | Skip inbound webhook signature verification. Development only — every adapter that reads this treats it as a bypass of sender authentication. | | `integrations.platforms` | `AGENT_NATIVE_INTEGRATION_PLATFORMS` | array | — | Integration platforms to mount, comma-separated, each matched against an adapter's `platform` id (slack, telegram, whatsapp, microsoft-teams, discord, google-docs, email). Unset mounts every adapter; a name no adapter provides throws at plugin init. | | `migration.releaseMigrations` | `AGENT_NATIVE_RELEASE_MIGRATIONS` | boolean | `false` | Treat database migrations as release-owned so request runtimes only probe an already-prepared schema. | diff --git a/packages/core/docs/content/authentication.mdx b/packages/core/docs/content/authentication.mdx index 5c110d165c6..03ed2b791c7 100644 --- a/packages/core/docs/content/authentication.mdx +++ b/packages/core/docs/content/authentication.mdx @@ -210,6 +210,24 @@ verification and the signup verification email is not sent. It does not change magic-link delivery. Use it only for QA or preview environments, and name test accounts with a `+qa` address (`name+qa@example.com`) so they are easy to identify. +## Email Verification Policy {#email-verification-policy} + +`AUTH_SKIP_EMAIL_VERIFICATION` is a local and QA convenience — a hosted +deployment ignores it. To state the policy for any environment, production +included, declare it in config: + +```ts +defineAppConfig({ auth: { requireEmailVerification: false } }); +``` + +The deployment alias is `AUTH_REQUIRE_EMAIL_VERIFICATION=0`. A declared value +outranks both `AUTH_SKIP_EMAIL_VERIFICATION` and the per-environment default. + +`false` accepts an unverified address as a login credential — anyone can claim +any email — so use it only where signup is already restricted another way. +`true` on a deployment with no email provider disables password signup instead, +because the verification could never be delivered. + ## Social Providers {#social-providers} Set environment variables to enable social login. Better Auth auto-detects them: @@ -477,27 +495,28 @@ The default `/_agent-native/google/auth-url` route does this automatically — o ## Environment Variables {#environment-variables} -| Variable | Purpose | -| --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | -| `BETTER_AUTH_SECRET` | Signing key for Better Auth (auto-generated if not set) | -| `AUTH_SKIP_EMAIL_VERIFICATION` | Set to `1` in QA/preview environments to let password-fallback signups proceed without verification; local dev/test skips by default | -| `AUTH_MAGIC_LINK` | Set to `0`, `false`, or `off` to force the email/password fallback even when outbound email is ready | -| `AUTH_DISABLED` | Set to `true` or `1` to skip login/signup; all requests run as one shared user (local dev/preview only — not for production with real users) | -| `AGENT_NATIVE_DISABLE_AUTO_DEV_ACCOUNT` | Set to `1` to disable localhost auto-sign-in on a fresh dev database | -| `AUTH_MODE` | `local` resolves CLI/agent identity only (which dev user `pnpm action` runs as); never a browser login bypass | -| `COOKIE_DOMAIN` | Opt into shared session cookies across same-database subdomains (see [Cookie Realms](#cookie-realms)) | -| `AGENT_NATIVE_WORKSPACE` | `1` runs in workspace mode — one shared session realm across workspace apps | -| `AGENT_NATIVE_SHARE_COOKIE_DOMAIN` | Set with `COOKIE_DOMAIN` to share one auth database across first-party subdomains | -| `OAUTH_STATE_SECRET` | Dedicated HMAC key for OAuth state envelopes (see [Security — OAuth State Signing](/docs/security#oauth-state)) | -| `GOOGLE_SIGN_IN_CLIENT_ID` | Preferred low-scope Google OAuth client ID for app login | -| `GOOGLE_SIGN_IN_CLIENT_SECRET` | Preferred low-scope Google OAuth secret for app login | -| `GOOGLE_CLIENT_ID` | Legacy Google login fallback, and provider OAuth client ID for Google API integrations | -| `GOOGLE_CLIENT_SECRET` | Legacy Google login fallback, and provider OAuth secret for Google API integrations | -| `GITHUB_CLIENT_ID` | Enable GitHub OAuth | -| `GITHUB_CLIENT_SECRET` | GitHub OAuth secret | -| `ACCESS_TOKEN` | Static bearer fallback for MCP/connect clients; not browser auth | -| `ACCESS_TOKENS` | Comma-separated static bearer fallbacks for MCP/connect clients; not browser auth | -| `A2A_SECRET` | Shared secret for JWT-signed A2A cross-app identity verification and, when present, MCP OAuth access-token signing | +| Variable | Purpose | +| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `BETTER_AUTH_SECRET` | Signing key for Better Auth (auto-generated if not set) | +| `AUTH_SKIP_EMAIL_VERIFICATION` | Set to `1` in QA/preview environments to let password-fallback signups proceed without verification; local dev/test skips by default | +| `AUTH_REQUIRE_EMAIL_VERIFICATION` | Set to `0` or `1` to declare the password-signup verification policy for any environment, production included; outranks `AUTH_SKIP_EMAIL_VERIFICATION` | +| `AUTH_MAGIC_LINK` | Set to `0`, `false`, or `off` to force the email/password fallback even when outbound email is ready | +| `AUTH_DISABLED` | Set to `true` or `1` to skip login/signup; all requests run as one shared user (local dev/preview only — not for production with real users) | +| `AGENT_NATIVE_DISABLE_AUTO_DEV_ACCOUNT` | Set to `1` to disable localhost auto-sign-in on a fresh dev database | +| `AUTH_MODE` | `local` resolves CLI/agent identity only (which dev user `pnpm action` runs as); never a browser login bypass | +| `COOKIE_DOMAIN` | Opt into shared session cookies across same-database subdomains (see [Cookie Realms](#cookie-realms)) | +| `AGENT_NATIVE_WORKSPACE` | `1` runs in workspace mode — one shared session realm across workspace apps | +| `AGENT_NATIVE_SHARE_COOKIE_DOMAIN` | Set with `COOKIE_DOMAIN` to share one auth database across first-party subdomains | +| `OAUTH_STATE_SECRET` | Dedicated HMAC key for OAuth state envelopes (see [Security — OAuth State Signing](/docs/security#oauth-state)) | +| `GOOGLE_SIGN_IN_CLIENT_ID` | Preferred low-scope Google OAuth client ID for app login | +| `GOOGLE_SIGN_IN_CLIENT_SECRET` | Preferred low-scope Google OAuth secret for app login | +| `GOOGLE_CLIENT_ID` | Legacy Google login fallback, and provider OAuth client ID for Google API integrations | +| `GOOGLE_CLIENT_SECRET` | Legacy Google login fallback, and provider OAuth secret for Google API integrations | +| `GITHUB_CLIENT_ID` | Enable GitHub OAuth | +| `GITHUB_CLIENT_SECRET` | GitHub OAuth secret | +| `ACCESS_TOKEN` | Static bearer fallback for MCP/connect clients; not browser auth | +| `ACCESS_TOKENS` | Comma-separated static bearer fallbacks for MCP/connect clients; not browser auth | +| `A2A_SECRET` | Shared secret for JWT-signed A2A cross-app identity verification and, when present, MCP OAuth access-token signing | ## What's next diff --git a/packages/core/docs/content/environment-variables.mdx b/packages/core/docs/content/environment-variables.mdx index 2106ec599e2..3ad5259c7b3 100644 --- a/packages/core/docs/content/environment-variables.mdx +++ b/packages/core/docs/content/environment-variables.mdx @@ -87,6 +87,7 @@ public app behavior, use the config paths above instead. | `AUTH_DISABLED` | Disables auth for local or preview-only work. Unset already means false. | `true` or `1`; never use on a public production app. | | `AUTH_MAGIC_LINK` | Controls the password-first versus magic-link sign-in path. | `0` keeps password-first sign-in; otherwise a configured email transport can enable magic links. | | `AUTH_SKIP_EMAIL_VERIFICATION` | Skips signup email verification for local QA or previews. | `1`; never use it to weaken production auth. | +| `AUTH_REQUIRE_EMAIL_VERIFICATION` | Declares whether password signup must verify the email address, in any environment. | `0` or `1`; outranks `AUTH_SKIP_EMAIL_VERIFICATION`. `0` accepts an unverified address as a login credential. | | `ACCESS_TOKEN` / `ACCESS_TOKENS` | Static bearer fallback for MCP and Connect clients. | One token or a delimited token list; these are not browser auth. | | `AGENT_PROD_CODE_EXECUTION` | Production code-execution policy. | `off`, `sandboxed`, or `trusted`. See [Production Code Execution](/docs/actions-agent-tools#production-code-execution). | | `AGENT_NATIVE_SSR_CACHE` | Deployment-wide public SSR shell cache policy. | Unset/`on`, `off`, or a duration such as `30s` or `5m`. See [SSR Caching](/docs/ssr-caching). | diff --git a/packages/core/docs/content/locales/ar-SA/authentication.mdx b/packages/core/docs/content/locales/ar-SA/authentication.mdx index 38f6b49493c..fd2468d6119 100644 --- a/packages/core/docs/content/locales/ar-SA/authentication.mdx +++ b/packages/core/docs/content/locales/ar-SA/authentication.mdx @@ -166,6 +166,24 @@ AUTH_SKIP_EMAIL_VERIFICATION=1 أو معاينة البيئات، وتسمية حسابات الاختبار بعنوان `+qa` (`name+qa@example.com`) حتى يسهل التعرف عليها. +## سياسة التحقق من البريد الإلكتروني {#email-verification-policy} + +`AUTH_SKIP_EMAIL_VERIFICATION` هو وسيلة راحة محلية ولضمان الجودة — وتتجاهله أي +عملية نشر مُستضافة. لتحديد السياسة لأي بيئة، بما في ذلك الإنتاج، صرِّح بها في +الإعدادات: + +```ts +defineAppConfig({ auth: { requireEmailVerification: false } }); +``` + +الاسم المستعار للنشر هو `AUTH_REQUIRE_EMAIL_VERIFICATION=0`. القيمة المُصرَّح بها +تتفوق على `AUTH_SKIP_EMAIL_VERIFICATION` وعلى الافتراضي الخاص بكل بيئة معًا. + +تقبل القيمة `false` عنوانًا غير مُتحقق منه كبيانات اعتماد لتسجيل الدخول — إذ +يمكن لأي شخص المطالبة بأي بريد إلكتروني — لذا استخدمها فقط حيث يكون الاشتراك +مقيدًا بالفعل بطريقة أخرى. أما `true` في عملية نشر بلا مزوّد بريد إلكتروني فتعطل +الاشتراك بكلمة مرور بدلًا من ذلك، لأن رسالة التحقق لن يمكن تسليمها أبدًا. + ## مقدمو الخدمات الاجتماعية {#social-providers} قم بتعيين متغيرات البيئة لتمكين تسجيل الدخول الاجتماعي. تكتشفها المصادقة الأفضل تلقائيًا: @@ -436,6 +454,7 @@ const state = encodeOAuthState({ | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `BETTER_AUTH_SECRET` | مفتاح التوقيع لمصادقة أفضل (يتم إنشاؤه تلقائيًا إذا لم يتم تعيينه) | | `AUTH_SKIP_EMAIL_VERIFICATION` | قم بالتعيين على `1` في بيئات ضمان الجودة/المعاينة للسماح بمتابعة عمليات الاشتراك في البريد الإلكتروني/كلمة المرور دون التحقق؛ يتم تخطي التطوير/الاختبار المحلي افتراضيًا | +| `AUTH_REQUIRE_EMAIL_VERIFICATION` | عيِّنه على `0` أو `1` لتحديد سياسة التحقق لاشتراكات كلمة المرور في أي بيئة، بما في ذلك الإنتاج؛ يتفوق على `AUTH_SKIP_EMAIL_VERIFICATION` | | `AUTH_DISABLED` | اضبط على `true` أو `1` لتخطي تسجيل الدخول/الاشتراك؛ يتم تشغيل جميع الطلبات كمستخدم واحد مشترك (للتطوير/المعاينة المحلية فقط - وليس للإنتاج مع مستخدمين حقيقيين) | | `AGENT_NATIVE_DISABLE_AUTO_DEV_ACCOUNT` | اضبط على `1` لتعطيل تسجيل الدخول التلقائي للمضيف المحلي في قاعدة بيانات جديدة للمطورين | | `AUTH_MODE` | يحل `local` هوية CLI/الوكيل فقط (الذي يعمل عليه مستخدم التطوير `pnpm action`)؛ لا تقم أبدًا بتجاوز تسجيل الدخول إلى المتصفح | diff --git a/packages/core/docs/content/locales/de-DE/authentication.mdx b/packages/core/docs/content/locales/de-DE/authentication.mdx index 5fb2ab16a41..8641bcbfb2c 100644 --- a/packages/core/docs/content/locales/de-DE/authentication.mdx +++ b/packages/core/docs/content/locales/de-DE/authentication.mdx @@ -166,6 +166,27 @@ Bestätigung und die Anmeldebestätigungs-E-Mail wird nicht gesendet. Verwenden oder Vorschauumgebungen und benennen Sie Testkonten mit einer `+qa`-Adresse (`name+qa@example.com`), damit sie leicht zu identifizieren sind. +## Richtlinie zur E-Mail-Überprüfung {#email-verification-policy} + +`AUTH_SKIP_EMAIL_VERIFICATION` ist eine lokale und QA-Bequemlichkeit — eine +gehostete Bereitstellung ignoriert sie. Um die Richtlinie für jede Umgebung +festzulegen, einschließlich der Produktion, deklarieren Sie sie in der +Konfiguration: + +```ts +defineAppConfig({ auth: { requireEmailVerification: false } }); +``` + +Der Bereitstellungsalias lautet `AUTH_REQUIRE_EMAIL_VERIFICATION=0`. Ein +deklarierter Wert übertrifft sowohl `AUTH_SKIP_EMAIL_VERIFICATION` als auch die +Standardeinstellung der jeweiligen Umgebung. + +`false` akzeptiert eine nicht überprüfte Adresse als Anmeldeinformation — jeder +kann jede E-Mail-Adresse beanspruchen — verwenden Sie es also nur dort, wo die +Anmeldung bereits anderweitig eingeschränkt ist. `true` deaktiviert auf einer +Bereitstellung ohne E-Mail-Anbieter stattdessen die Passwortanmeldung, da die +Überprüfung nie zugestellt werden könnte. + ## Soziale Anbieter {#social-providers} Legen Sie Umgebungsvariablen fest, um die soziale Anmeldung zu ermöglichen. Better Auth erkennt sie automatisch: @@ -438,6 +459,7 @@ Die Standardroute `/_agent-native/google/auth-url` führt dies automatisch aus | --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `BETTER_AUTH_SECRET` | Signaturschlüssel für Better Auth (automatisch generiert, wenn nicht festgelegt) | | `AUTH_SKIP_EMAIL_VERIFICATION` | In QA-/Vorschauumgebungen auf `1` setzen, damit E-Mail-/Passwort-Anmeldungen ohne Überprüfung durchgeführt werden können. Lokale Entwicklung/Test überspringt standardmäßig | +| `AUTH_REQUIRE_EMAIL_VERIFICATION` | Auf `0` oder `1` setzen, um die Überprüfungsrichtlinie für die Passwortanmeldung in jeder Umgebung festzulegen, einschließlich der Produktion; übertrifft `AUTH_SKIP_EMAIL_VERIFICATION` | | `AUTH_DISABLED` | Auf `true` oder `1` einstellen, um Anmeldung/Registrierung zu überspringen; Alle Anfragen werden von einem gemeinsamen Benutzer ausgeführt (nur lokale Entwicklung/Vorschau – nicht für die Produktion mit echten Benutzern) | | `AGENT_NATIVE_DISABLE_AUTO_DEV_ACCOUNT` | Auf `1` einstellen, um die automatische Anmeldung von Localhost in einer neuen Entwicklungsdatenbank zu deaktivieren | | `AUTH_MODE` | `local` löst nur die CLI/Agent-Identität auf (unter der der Entwicklerbenutzer `pnpm action` ausgeführt wird); niemals eine Browser-Anmeldeumgehung | diff --git a/packages/core/docs/content/locales/es-ES/authentication.mdx b/packages/core/docs/content/locales/es-ES/authentication.mdx index e061eba06cc..614a502bc28 100644 --- a/packages/core/docs/content/locales/es-ES/authentication.mdx +++ b/packages/core/docs/content/locales/es-ES/authentication.mdx @@ -166,6 +166,26 @@ no se envía la verificación y el correo electrónico de verificación de regis o obtener una vista previa de los entornos y nombrar cuentas de prueba con una dirección `+qa` (`name+qa@example.com`) para que sean fáciles de identificar. +## Política de verificación de correo electrónico {#email-verification-policy} + +`AUTH_SKIP_EMAIL_VERIFICATION` es una comodidad local y de control de calidad: +una implementación alojada la ignora. Para declarar la política en cualquier +entorno, incluida la producción, decláresela en la configuración: + +```ts +defineAppConfig({ auth: { requireEmailVerification: false } }); +``` + +El alias de implementación es `AUTH_REQUIRE_EMAIL_VERIFICATION=0`. Un valor +declarado prevalece tanto sobre `AUTH_SKIP_EMAIL_VERIFICATION` como sobre el +valor predeterminado de cada entorno. + +`false` acepta una dirección no verificada como credencial de inicio de sesión +—cualquiera puede reclamar cualquier correo electrónico—, así que úselo solo +donde el registro ya esté restringido de otra manera. `true` en una +implementación sin proveedor de correo electrónico deshabilita el registro con +contraseña, porque la verificación nunca podría entregarse. + ## Proveedores sociales {#social-providers} Establezca variables de entorno para habilitar el inicio de sesión social. Better Auth los detecta automáticamente: @@ -438,6 +458,7 @@ La ruta `/_agent-native/google/auth-url` predeterminada hace esto automáticamen | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `BETTER_AUTH_SECRET` | Clave de firma para una mejor autenticación (generada automáticamente si no está configurada) | | `AUTH_SKIP_EMAIL_VERIFICATION` | Establezca en `1` en entornos de control de calidad/vista previa para permitir que los registros de correo electrónico/contraseña se realicen sin verificación; El desarrollo/prueba local se salta de forma predeterminada | +| `AUTH_REQUIRE_EMAIL_VERIFICATION` | Establezca en `0` o `1` para declarar la política de verificación del registro con contraseña en cualquier entorno, incluida la producción; prevalece sobre `AUTH_SKIP_EMAIL_VERIFICATION` | | `AUTH_DISABLED` | Establezca en `true` o `1` para omitir el inicio de sesión/registro; todas las solicitudes se ejecutan como un usuario compartido (solo desarrollo/vista previa local, no para producción con usuarios reales) | | `AGENT_NATIVE_DISABLE_AUTO_DEV_ACCOUNT` | Establezca en `1` para deshabilitar el inicio de sesión automático de localhost en una base de datos de desarrollo nueva | | `AUTH_MODE` | `local` resuelve solo la identidad de CLI/agente (con la que se ejecuta el usuario de desarrollo `pnpm action`); nunca una omisión de inicio de sesión del navegador | diff --git a/packages/core/docs/content/locales/fr-FR/authentication.mdx b/packages/core/docs/content/locales/fr-FR/authentication.mdx index 5c52a8ac127..a7cbc3d861b 100644 --- a/packages/core/docs/content/locales/fr-FR/authentication.mdx +++ b/packages/core/docs/content/locales/fr-FR/authentication.mdx @@ -166,6 +166,26 @@ vérification et l'e-mail de vérification de l'inscription n'est pas envoyé. U ou prévisualisez les environnements et nommez les comptes de test avec une adresse `+qa` (`name+qa@example.com`) pour qu'ils soient faciles à identifier. +## Politique de vérification des e-mails {#email-verification-policy} + +`AUTH_SKIP_EMAIL_VERIFICATION` est une commodité locale et de contrôle qualité : +un déploiement hébergé l'ignore. Pour définir la politique dans n'importe quel +environnement, production comprise, déclarez-la dans la configuration : + +```ts +defineAppConfig({ auth: { requireEmailVerification: false } }); +``` + +L'alias de déploiement est `AUTH_REQUIRE_EMAIL_VERIFICATION=0`. Une valeur +déclarée l'emporte à la fois sur `AUTH_SKIP_EMAIL_VERIFICATION` et sur la +valeur par défaut de chaque environnement. + +`false` accepte une adresse non vérifiée comme identifiant de connexion — +n'importe qui peut revendiquer n'importe quel e-mail — ne l'utilisez donc que +là où l'inscription est déjà restreinte autrement. `true` sur un déploiement +sans fournisseur d'e-mail désactive plutôt l'inscription par mot de passe, car +la vérification ne pourrait jamais être délivrée. + ## Prestataires sociaux {#social-providers} Définissez les variables d'environnement pour activer la connexion sociale. Better Auth les détecte automatiquement : @@ -438,6 +458,7 @@ La route `/_agent-native/google/auth-url` par défaut le fait automatiquement : | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `BETTER_AUTH_SECRET` | Clé de signature pour une meilleure authentification (générée automatiquement si elle n'est pas définie) | | `AUTH_SKIP_EMAIL_VERIFICATION` | Défini sur `1` dans les environnements QA/preview pour permettre aux inscriptions par e-mail/mot de passe de se dérouler sans vérification ; le développement/test local est ignoré par défaut | +| `AUTH_REQUIRE_EMAIL_VERIFICATION` | Défini sur `0` ou `1` pour déclarer la politique de vérification de l'inscription par mot de passe dans n'importe quel environnement, production comprise ; l'emporte sur `AUTH_SKIP_EMAIL_VERIFICATION` | | `AUTH_DISABLED` | Définissez sur `true` ou `1` pour ignorer la connexion/l'inscription ; toutes les requêtes s'exécutent en tant qu'utilisateur partagé (développement/aperçu local uniquement – pas pour la production avec de vrais utilisateurs) | | `AGENT_NATIVE_DISABLE_AUTO_DEV_ACCOUNT` | Définissez sur `1` pour désactiver la connexion automatique de localhost sur une nouvelle base de données de développement | | `AUTH_MODE` | `local` résout uniquement l'identité de CLI/agent (sous quel utilisateur de développement `pnpm action` s'exécute) ; jamais de contournement de connexion au navigateur | diff --git a/packages/core/docs/content/locales/hi-IN/authentication.mdx b/packages/core/docs/content/locales/hi-IN/authentication.mdx index 1c2dbe9f1de..fce9d497d58 100644 --- a/packages/core/docs/content/locales/hi-IN/authentication.mdx +++ b/packages/core/docs/content/locales/hi-IN/authentication.mdx @@ -166,6 +166,25 @@ AUTH_SKIP_EMAIL_VERIFICATION=1 या परिवेश का पूर्वावलोकन करें, और `+qa` पते के साथ परीक्षण खातों को नाम दें (`name+qa@example.com`) ताकि उन्हें पहचानना आसान हो। +## ईमेल सत्यापन नीति {#email-verification-policy} + +`AUTH_SKIP_EMAIL_VERIFICATION` एक स्थानीय और QA सुविधा है — होस्ट की गई तैनाती +इसे अनदेखा करती है। उत्पादन सहित किसी भी वातावरण के लिए नीति बताने हेतु, इसे +कॉन्फ़िगरेशन में घोषित करें: + +```ts +defineAppConfig({ auth: { requireEmailVerification: false } }); +``` + +तैनाती उपनाम `AUTH_REQUIRE_EMAIL_VERIFICATION=0` है। घोषित मान +`AUTH_SKIP_EMAIL_VERIFICATION` और प्रति-वातावरण डिफ़ॉल्ट, दोनों से ऊपर रहता है। + +`false` एक असत्यापित पते को लॉगिन क्रेडेंशियल के रूप में स्वीकार करता है — कोई भी +किसी भी ईमेल का दावा कर सकता है — इसलिए इसका उपयोग केवल वहीं करें जहाँ साइनअप +पहले से किसी अन्य तरीके से सीमित हो। बिना ईमेल प्रदाता वाली तैनाती पर `true` +इसके बजाय पासवर्ड साइनअप को अक्षम कर देता है, क्योंकि सत्यापन कभी वितरित नहीं +हो सकता। + ## सामाजिक प्रदाता {#social-providers} सामाजिक लॉगिन सक्षम करने के लिए पर्यावरण चर सेट करें। Better Auth उनका स्वत: पता लगाता है: @@ -438,6 +457,7 @@ const state = encodeOAuthState({ | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `BETTER_AUTH_SECRET` | बेहतर प्रमाणीकरण के लिए हस्ताक्षर कुंजी (यदि सेट नहीं है तो स्वतः उत्पन्न) | | `AUTH_SKIP_EMAIL_VERIFICATION` | ईमेल/पासवर्ड साइनअप को सत्यापन के बिना आगे बढ़ने देने के लिए QA/पूर्वावलोकन वातावरण में `1` पर सेट करें; स्थानीय विकास/परीक्षण डिफ़ॉल्ट रूप से स्किप हो जाता है | +| `AUTH_REQUIRE_EMAIL_VERIFICATION` | उत्पादन सहित किसी भी वातावरण के लिए पासवर्ड साइनअप की सत्यापन नीति घोषित करने हेतु `0` या `1` सेट करें; यह `AUTH_SKIP_EMAIL_VERIFICATION` से ऊपर रहता है | | `AUTH_DISABLED` | लॉगिन/साइनअप छोड़ने के लिए `true` या `1` पर सेट करें; सभी अनुरोध एक साझा उपयोगकर्ता के रूप में चलते हैं (केवल स्थानीय विकास/पूर्वावलोकन - वास्तविक उपयोगकर्ताओं के साथ उत्पादन के लिए नहीं) | | `AGENT_NATIVE_DISABLE_AUTO_DEV_ACCOUNT` | ताज़ा डेव डेटाबेस पर लोकलहोस्ट ऑटो-साइन-इन को अक्षम करने के लिए `1` पर सेट करें | | `AUTH_MODE` | `local` केवल CLI/एजेंट पहचान का समाधान करता है (जो देव उपयोगकर्ता `pnpm action` के रूप में चलता है); ब्राउज़र लॉगिन को कभी भी बायपास न करें | diff --git a/packages/core/docs/content/locales/ja-JP/authentication.mdx b/packages/core/docs/content/locales/ja-JP/authentication.mdx index f1bcea879f2..323d117ab64 100644 --- a/packages/core/docs/content/locales/ja-JP/authentication.mdx +++ b/packages/core/docs/content/locales/ja-JP/authentication.mdx @@ -166,6 +166,24 @@ AUTH_SKIP_EMAIL_VERIFICATION=1 またはプレビュー環境で、`+qa` アドレスを持つテスト アカウントに名前を付けます (`name+qa@example.com`) なので、簡単に識別できます。 +## メール検証ポリシー {#email-verification-policy} + +`AUTH_SKIP_EMAIL_VERIFICATION` はローカルおよび QA 向けの利便性であり、ホスト +された環境では無視されます。本番環境を含むあらゆる環境でポリシーを明示するには、 +設定で宣言します: + +```ts +defineAppConfig({ auth: { requireEmailVerification: false } }); +``` + +デプロイ用のエイリアスは `AUTH_REQUIRE_EMAIL_VERIFICATION=0` です。宣言された値 +は `AUTH_SKIP_EMAIL_VERIFICATION` と環境ごとの既定値の両方より優先されます。 + +`false` は未検証のアドレスをログイン資格情報として受け入れます。誰でも任意のメール +アドレスを主張できるため、サインアップが別の方法ですでに制限されている場合にのみ +使用してください。メールプロバイダーのないデプロイで `true` を設定した場合は、検証 +を配信できないため、代わりにパスワードによるサインアップが無効になります。 + ## ソーシャル プロバイダー {#social-providers} ソーシャル ログインを有効にするために環境変数を設定します。 Better Auth はそれらを自動検出します: @@ -438,6 +456,7 @@ const state = encodeOAuthState({ | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `BETTER_AUTH_SECRET` | Better Auth 用の署名キー (設定されていない場合は自動生成されます) | | `AUTH_SKIP_EMAIL_VERIFICATION` | QA/プレビュー環境で `1` に設定すると、電子メール/パスワードのサインアップが検証なしで続行できるようになります。ローカルの開発/テストはデフォルトでスキップされます | +| `AUTH_REQUIRE_EMAIL_VERIFICATION` | `0` または `1` を設定すると、本番環境を含むあらゆる環境でパスワードサインアップの検証ポリシーを宣言できます。`AUTH_SKIP_EMAIL_VERIFICATION` より優先されます | | `AUTH_DISABLED` | ログイン/サインアップをスキップするには、`true` または `1` に設定します。すべてのリクエストは 1 人の共有ユーザーとして実行されます (ローカルの開発/プレビューのみ。実際のユーザーによる本番環境では使用できません) | | `AGENT_NATIVE_DISABLE_AUTO_DEV_ACCOUNT` | 新しい開発データベースでローカルホストの自動サインインを無効にするには、`1` に設定します | | `AUTH_MODE` | `local` は、CLI/エージェント ID のみを解決します (開発ユーザー `pnpm action` が実行される)。ブラウザのログインをバイパスしない | diff --git a/packages/core/docs/content/locales/ko-KR/authentication.mdx b/packages/core/docs/content/locales/ko-KR/authentication.mdx index 11d91116130..ad294748b52 100644 --- a/packages/core/docs/content/locales/ko-KR/authentication.mdx +++ b/packages/core/docs/content/locales/ko-KR/authentication.mdx @@ -166,6 +166,24 @@ AUTH_SKIP_EMAIL_VERIFICATION=1 또는 환경을 미리 보고 `+qa` 주소로 테스트 계정 이름을 지정 (`name+qa@example.com`) 쉽게 식별할 수 있습니다. +## 이메일 확인 정책 {#email-verification-policy} + +`AUTH_SKIP_EMAIL_VERIFICATION`은 로컬 및 QA용 편의 기능이며 호스팅된 배포에서는 +무시됩니다. 프로덕션을 포함한 모든 환경에 대해 정책을 명시하려면 구성에서 +선언하세요: + +```ts +defineAppConfig({ auth: { requireEmailVerification: false } }); +``` + +배포 별칭은 `AUTH_REQUIRE_EMAIL_VERIFICATION=0`입니다. 선언된 값은 +`AUTH_SKIP_EMAIL_VERIFICATION`과 환경별 기본값보다 모두 우선합니다. + +`false`는 확인되지 않은 주소를 로그인 자격 증명으로 허용합니다. 누구나 어떤 +이메일이든 사용할 수 있으므로 가입이 이미 다른 방법으로 제한된 곳에서만 +사용하세요. 이메일 공급자가 없는 배포에서 `true`로 설정하면 확인을 전달할 수 +없으므로 대신 비밀번호 가입이 비활성화됩니다. + ## 소셜 제공자 {#social-providers} 소셜 로그인을 활성화하려면 환경 변수를 설정하세요. Better Auth는 이를 자동으로 감지합니다: @@ -438,6 +456,7 @@ const state = encodeOAuthState({ | --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `BETTER_AUTH_SECRET` | 더 나은 인증을 위한 서명 키(설정되지 않은 경우 자동 생성) | | `AUTH_SKIP_EMAIL_VERIFICATION` | QA/미리보기 환경에서 `1`로 설정하면 확인 없이 이메일/비밀번호 가입이 진행될 수 있습니다. 기본적으로 로컬 개발/테스트 건너뛰기 | +| `AUTH_REQUIRE_EMAIL_VERIFICATION` | `0` 또는 `1`로 설정하면 프로덕션을 포함한 모든 환경에서 비밀번호 가입 확인 정책을 선언합니다. `AUTH_SKIP_EMAIL_VERIFICATION`보다 우선합니다 | | `AUTH_DISABLED` | 로그인/가입을 건너뛰려면 `true` 또는 `1`로 설정하세요. 모든 요청은 하나의 공유 사용자로 실행됩니다(로컬 개발/미리 보기에만 해당 - 실제 사용자가 있는 프로덕션에는 해당되지 않음) | | `AGENT_NATIVE_DISABLE_AUTO_DEV_ACCOUNT` | 새로운 개발 데이터베이스에서 로컬 호스트 자동 로그인을 비활성화하려면 `1`로 설정하세요. | | `AUTH_MODE` | `local`는 CLI/에이전트 ID만 확인합니다(개발자 사용자 `pnpm action`가 실행되는 ID). 브라우저 로그인 우회는 절대 안 됩니다 | diff --git a/packages/core/docs/content/locales/pt-BR/authentication.mdx b/packages/core/docs/content/locales/pt-BR/authentication.mdx index 0a9322ecd79..1f6c029651d 100644 --- a/packages/core/docs/content/locales/pt-BR/authentication.mdx +++ b/packages/core/docs/content/locales/pt-BR/authentication.mdx @@ -166,6 +166,26 @@ verificação e o e-mail de verificação de inscrição não são enviados. Use ou visualize ambientes e nomeie contas de teste com um endereço `+qa` (`name+qa@example.com`) para que sejam fáceis de identificar. +## Política de verificação de e-mail {#email-verification-policy} + +`AUTH_SKIP_EMAIL_VERIFICATION` é uma conveniência local e de controle de +qualidade — uma implantação hospedada a ignora. Para declarar a política em +qualquer ambiente, inclusive produção, declare-a na configuração: + +```ts +defineAppConfig({ auth: { requireEmailVerification: false } }); +``` + +O alias de implantação é `AUTH_REQUIRE_EMAIL_VERIFICATION=0`. Um valor +declarado prevalece sobre `AUTH_SKIP_EMAIL_VERIFICATION` e sobre o padrão de +cada ambiente. + +`false` aceita um endereço não verificado como credencial de login — qualquer +pessoa pode reivindicar qualquer e-mail —, portanto use-o apenas onde a +inscrição já esteja restrita de outra forma. `true` em uma implantação sem +provedor de e-mail desativa a inscrição por senha, porque a verificação nunca +poderia ser entregue. + ## Provedores Sociais {#social-providers} Defina variáveis de ambiente para ativar o login social. O Better Auth os detecta automaticamente: @@ -438,6 +458,7 @@ A rota `/_agent-native/google/auth-url` padrão faz isso automaticamente – sub | --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `BETTER_AUTH_SECRET` | Chave de assinatura para Better Auth (gerada automaticamente se não for definida) | | `AUTH_SKIP_EMAIL_VERIFICATION` | Defina como `1` em ambientes de controle de qualidade/visualização para permitir que inscrições de e-mail/senha continuem sem verificação; desenvolvimento/teste local ignora por padrão | +| `AUTH_REQUIRE_EMAIL_VERIFICATION` | Defina como `0` ou `1` para declarar a política de verificação da inscrição por senha em qualquer ambiente, inclusive produção; prevalece sobre `AUTH_SKIP_EMAIL_VERIFICATION` | | `AUTH_DISABLED` | Defina como `true` ou `1` para ignorar o login/inscrição; todas as solicitações são executadas como um usuário compartilhado (somente desenvolvimento/visualização local — não para produção com usuários reais) | | `AGENT_NATIVE_DISABLE_AUTO_DEV_ACCOUNT` | Defina como `1` para desativar o login automático do host local em um novo banco de dados de desenvolvimento | | `AUTH_MODE` | `local` resolve apenas a identidade CLI/agente (como o usuário desenvolvedor `pnpm action` é executado); nunca um desvio de login do navegador | diff --git a/packages/core/docs/content/locales/zh-CN/authentication.mdx b/packages/core/docs/content/locales/zh-CN/authentication.mdx index 94c39e51a5a..17013c264eb 100644 --- a/packages/core/docs/content/locales/zh-CN/authentication.mdx +++ b/packages/core/docs/content/locales/zh-CN/authentication.mdx @@ -167,6 +167,22 @@ AUTH_SKIP_EMAIL_VERIFICATION=1 或预览环境,并使用 `+qa` 地址命名测试帐户 (`name+qa@example.com`),因此很容易识别。 +## 电子邮件验证策略 {#email-verification-policy} + +`AUTH_SKIP_EMAIL_VERIFICATION` 只是本地和 QA 的便利设置,托管部署会忽略它。要为 +任何环境(包括生产环境)声明该策略,请在配置中声明: + +```ts +defineAppConfig({ auth: { requireEmailVerification: false } }); +``` + +部署别名为 `AUTH_REQUIRE_EMAIL_VERIFICATION=0`。声明的值优先于 +`AUTH_SKIP_EMAIL_VERIFICATION` 和各环境的默认值。 + +`false` 会将未经验证的地址作为登录凭据接受——任何人都可以声称拥有任何电子邮件 +地址——因此仅在注册已通过其他方式受限时使用。在没有电子邮件提供商的部署上设置 +`true` 则会禁用密码注册,因为验证邮件永远无法送达。 + ## 社交提供商 {#social-providers} 设置环境变量以启用社交登录。 Better Auth 自动检测它们: @@ -439,6 +455,7 @@ const state = encodeOAuthState({ | --------------------------------------- | ------------------------------------------------------------------------------------------------------------ | | `BETTER_AUTH_SECRET` | 更好的身份验证签名密钥(如果未设置,则自动生成) | | `AUTH_SKIP_EMAIL_VERIFICATION` | 在 QA/预览环境中设置为 `1`,让电子邮件/密码注册无需验证即可继续进行;默认情况下跳过本地开发/测试 | +| `AUTH_REQUIRE_EMAIL_VERIFICATION` | 设置为 `0` 或 `1` 可为任何环境(包括生产环境)声明密码注册的验证策略;优先于 `AUTH_SKIP_EMAIL_VERIFICATION` | | `AUTH_DISABLED` | 设置为`true`或`1`跳过登录/注册;所有请求都作为一个共享用户运行(仅限本地开发/预览 - 不适用于真实用户的生产) | | `AGENT_NATIVE_DISABLE_AUTO_DEV_ACCOUNT` | 设置为 `1` 以在新的开发数据库上禁用本地主机自动登录 | | `AUTH_MODE` | `local` 仅解析 CLI/代理身份(开发用户 `pnpm action` 作为其运行);绝不绕过浏览器登录 | diff --git a/packages/core/docs/content/locales/zh-TW/authentication.mdx b/packages/core/docs/content/locales/zh-TW/authentication.mdx index 53c8e09b460..21c3f290f2d 100644 --- a/packages/core/docs/content/locales/zh-TW/authentication.mdx +++ b/packages/core/docs/content/locales/zh-TW/authentication.mdx @@ -167,6 +167,22 @@ AUTH_SKIP_EMAIL_VERIFICATION=1 或預覽環境,並使用 `+qa` 地址命名測試帳戶 (`name+qa@example.com`),因此很容易識別。 +## 電子郵件驗證政策 {#email-verification-policy} + +`AUTH_SKIP_EMAIL_VERIFICATION` 只是本機和 QA 的便利設定,託管部署會忽略它。若要 +為任何環境(包括正式環境)宣告該政策,請在設定中宣告: + +```ts +defineAppConfig({ auth: { requireEmailVerification: false } }); +``` + +部署別名為 `AUTH_REQUIRE_EMAIL_VERIFICATION=0`。宣告的值優先於 +`AUTH_SKIP_EMAIL_VERIFICATION` 和各環境的預設值。 + +`false` 會將未經驗證的位址當作登入憑證接受——任何人都可以宣稱擁有任何電子郵件 +位址——因此僅在註冊已透過其他方式受限時使用。在沒有電子郵件供應商的部署上設定 +`true` 則會停用密碼註冊,因為驗證信永遠無法送達。 + ## 社交提供者 {#social-providers} 設定環境變數以啟用社交登入。 Better Auth 自動偵測它們: @@ -439,6 +455,7 @@ const state = encodeOAuthState({ | --------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | | `BETTER_AUTH_SECRET` | 更好的驗證簽名金鑰(如果未設定,則自動生成) | | `AUTH_SKIP_EMAIL_VERIFICATION` | 在 QA/預覽環境中設定為 `1`,讓電子郵件/密碼註冊無需驗證即可繼續進行;預設情況下跳過本機開發/測試 | +| `AUTH_REQUIRE_EMAIL_VERIFICATION` | 設定為 `0` 或 `1` 可為任何環境(包括正式環境)宣告密碼註冊的驗證政策;優先於 `AUTH_SKIP_EMAIL_VERIFICATION` | | `AUTH_DISABLED` | 設定為`true`或`1`跳過登入/註冊;所有請求都作為一個共用使用者執行(僅限本機開發/預覽 - 不適用於真實使用者的正式環境) | | `AGENT_NATIVE_DISABLE_AUTO_DEV_ACCOUNT` | 設定為 `1` 以在新的開發資料庫上停用本機主機自動登入 | | `AUTH_MODE` | `local` 僅解析 CLI/代理身分(開發使用者 `pnpm action` 作為其執行);絕不繞過瀏覽器登入 | diff --git a/packages/core/src/app-config/auth.ts b/packages/core/src/app-config/auth.ts index ad930eae58b..8e1923a8685 100644 --- a/packages/core/src/app-config/auth.ts +++ b/packages/core/src/app-config/auth.ts @@ -5,4 +5,11 @@ export const authConfig = z.object({ env: "AGENT_NATIVE_DISABLE_DESKTOP_SSO_FALLBACK", doc: "Disable the loopback Desktop SSO fallback in development so isolated acceptance runs can use their configured local identity. Ignored in production.", }), + // Deliberately optional rather than defaulted: unset means "derive from the + // deployment", which is not a boolean. A default here would erase the + // difference between an operator who chose a policy and one who never spoke. + requireEmailVerification: z.boolean().optional().meta({ + env: "AUTH_REQUIRE_EMAIL_VERIFICATION", + doc: "Whether password signup must verify the email address before it gets a session. Unset derives it: hosted deployments require it, local development skips it. Setting it false accepts an unverified email as a login credential, including in production.", + }), }); diff --git a/packages/core/src/server/auth.spec.ts b/packages/core/src/server/auth.spec.ts index 69f849a5d22..01d9f2d5285 100644 --- a/packages/core/src/server/auth.spec.ts +++ b/packages/core/src/server/auth.spec.ts @@ -140,6 +140,91 @@ describe("server/auth", () => { }, 15_000); }); + describe("auth.requireEmailVerification", () => { + afterEach(async () => { + const { resetAppConfigForTests } = await import("../app-config/index.js"); + resetAppConfigForTests(); + }); + + it("turns verification off in hosted production when declared false", async () => { + vi.stubEnv("NODE_ENV", "production"); + vi.stubEnv("AUTH_REQUIRE_EMAIL_VERIFICATION", "0"); + const { resolveEmailPasswordAuthPolicy } = + await import("./better-auth-instance.js"); + + expect(resolveEmailPasswordAuthPolicy(true)).toEqual({ + requireEmailVerification: false, + disableSignUp: false, + }); + }, 15_000); + + it("also lifts the hosted no-email signup lock, since it is the same decision", async () => { + vi.stubEnv("NODE_ENV", "production"); + vi.stubEnv("AUTH_REQUIRE_EMAIL_VERIFICATION", "0"); + const { resolveEmailPasswordAuthPolicy } = + await import("./better-auth-instance.js"); + + expect(resolveEmailPasswordAuthPolicy(false)).toEqual({ + requireEmailVerification: false, + disableSignUp: false, + }); + }, 15_000); + + it("outranks AUTH_SKIP_EMAIL_VERIFICATION in local development", async () => { + vi.stubEnv("NODE_ENV", "development"); + vi.stubEnv("AUTH_SKIP_EMAIL_VERIFICATION", "1"); + vi.stubEnv("AUTH_REQUIRE_EMAIL_VERIFICATION", "1"); + const { resolveEmailPasswordAuthPolicy } = + await import("./better-auth-instance.js"); + + expect(resolveEmailPasswordAuthPolicy(true)).toEqual({ + requireEmailVerification: true, + disableSignUp: false, + }); + }, 15_000); + + it("refuses signup when it requires a verification no provider can deliver", async () => { + vi.stubEnv("NODE_ENV", "development"); + vi.stubEnv("AUTH_REQUIRE_EMAIL_VERIFICATION", "1"); + const { resolveEmailPasswordAuthPolicy } = + await import("./better-auth-instance.js"); + + expect(resolveEmailPasswordAuthPolicy(false)).toEqual({ + requireEmailVerification: false, + disableSignUp: true, + }); + }, 15_000); + + it("is settable from defineAppConfig, which beats the env alias", async () => { + vi.stubEnv("NODE_ENV", "production"); + vi.stubEnv("AUTH_REQUIRE_EMAIL_VERIFICATION", "1"); + const { defineAppConfig } = await import("../app-config/index.js"); + defineAppConfig({ auth: { requireEmailVerification: false } }); + const { resolveEmailPasswordAuthPolicy } = + await import("./better-auth-instance.js"); + + expect(resolveEmailPasswordAuthPolicy(true)).toEqual({ + requireEmailVerification: false, + disableSignUp: false, + }); + }, 15_000); + + it("leaves the derived policy alone when it is unset", async () => { + vi.stubEnv("NODE_ENV", "production"); + const { resolveEmailPasswordAuthPolicy } = + await import("./better-auth-instance.js"); + + expect(resolveEmailPasswordAuthPolicy(true)).toEqual({ + requireEmailVerification: true, + disableSignUp: false, + }); + expect(resolveEmailPasswordAuthPolicy(false)).toEqual({ + requireEmailVerification: false, + disableSignUp: true, + }); + }, 15_000); + }); + describe("resolveAuthLoginMode", () => { it("defaults to magic link only when email is ready", async () => { const { resolveAuthLoginMode } = diff --git a/packages/core/src/server/better-auth-instance.ts b/packages/core/src/server/better-auth-instance.ts index 7eea0a31054..3f268ccaeb6 100644 --- a/packages/core/src/server/better-auth-instance.ts +++ b/packages/core/src/server/better-auth-instance.ts @@ -26,6 +26,7 @@ import { integer as sqliteInteger, } from "drizzle-orm/sqlite-core"; +import { getAppConfig } from "../app-config/index.js"; import { TEMPLATES } from "../cli/templates-meta.js"; import { getDbExec, isPostgres } from "../db/client.js"; import { @@ -395,6 +396,19 @@ export function resolveEmailPasswordAuthPolicy(emailConfigured: boolean): { requireEmailVerification: boolean; disableSignUp: boolean; } { + const declared = getAppConfig().auth.requireEmailVerification; + if (declared !== undefined) { + // A declared policy is the whole policy — it outranks both the hosted + // derivation below and AUTH_SKIP_EMAIL_VERIFICATION. Choosing `false` is + // choosing to accept an unverified email as a login credential, so the + // signup lock that exists to prevent exactly that comes off with it. + return { + requireEmailVerification: declared && emailConfigured, + // Verification that no provider can deliver would strand every new + // account on an unverifiable signup, so refuse the signup instead. + disableSignUp: declared && !emailConfigured, + }; + } const hosted = process.env.NODE_ENV === "production" || isDeployPreview(); return { requireEmailVerification: diff --git a/packages/core/src/vite/client.spec.ts b/packages/core/src/vite/client.spec.ts index b34aa5c1ae0..41233a740aa 100644 --- a/packages/core/src/vite/client.spec.ts +++ b/packages/core/src/vite/client.spec.ts @@ -1666,6 +1666,38 @@ describe("agentNative Vite plugin preset", () => { }); }); + it("does not re-emit Vite's deprecated rollupOptions alias", async () => { + const plugins = flatPlugins(agentNative()); + const configPlugin = plugins.find((p) => p?.name === "agent-native-config"); + + // Vite 8 hands plugins a config where `rollupOptions` is a getter alias of + // `rolldownOptions`. Spreading it back out alongside our own + // `rolldownOptions` makes Vite warn that this plugin set both. + const aliasSection = (rolldownOptions: unknown) => { + const section: any = { rolldownOptions }; + Object.defineProperty(section, "rollupOptions", { + get: () => section.rolldownOptions, + enumerable: true, + configurable: true, + }); + return section; + }; + + const config = (await configPlugin.config( + { + build: aliasSection({}), + optimizeDeps: aliasSection({ plugins: [{ name: "app-dep-plugin" }] }), + }, + { command: "serve", mode: "development" }, + )) as any; + + expect(Object.hasOwn(config.optimizeDeps, "rollupOptions")).toBe(false); + expect(Object.hasOwn(config.build, "rollupOptions")).toBe(false); + expect( + config.optimizeDeps.rolldownOptions.plugins.map((p: any) => p.name), + ).toEqual(["app-dep-plugin", "agent-native:no-dep-prebundle-sourcemaps"]); + }); + it("restores dep prebundle sourcemaps when AGENT_NATIVE_DEP_SOURCEMAPS=1", async () => { const previous = process.env.AGENT_NATIVE_DEP_SOURCEMAPS; process.env.AGENT_NATIVE_DEP_SOURCEMAPS = "1"; diff --git a/packages/core/src/vite/client.ts b/packages/core/src/vite/client.ts index 1243ee7c3ed..3d16a9e4ac8 100644 --- a/packages/core/src/vite/client.ts +++ b/packages/core/src/vite/client.ts @@ -3695,6 +3695,15 @@ function createAgentNativeConfig( const forcePollingWatch = process.env.CHOKIDAR_USEPOLLING === "1"; const pollingWatchInterval = Number(process.env.CHOKIDAR_INTERVAL ?? 1000); const userWatch = userConfig.server?.watch ?? {}; + // Vite 8 defines `rollupOptions` on `build`/`optimizeDeps` as a getter alias + // of `rolldownOptions`. Spreading the section copies the alias as a plain own + // property, so returning our own `rolldownOptions` alongside it makes the two + // diverge and Vite warns that this plugin set both — then ignores the + // `rollupOptions` half regardless. Drop the alias from what we spread back. + const { rollupOptions: _buildRollupOptionsAlias, ...userBuild } = + userConfig.build ?? {}; + const { rollupOptions: _depsRollupOptionsAlias, ...userOptimizeDeps } = + userConfig.optimizeDeps ?? {}; return { logLevel: @@ -3810,7 +3819,7 @@ function createAgentNativeConfig( }, }, build: { - ...(userConfig.build ?? {}), + ...userBuild, outDir: options.outDir ?? userConfig.build?.outDir ?? "dist/spa", // Vite 8 defaults CSS minification to Lightning CSS, which collapses a // `backdrop-filter` + `-webkit-backdrop-filter` pair down to only the @@ -3897,7 +3906,7 @@ function createAgentNativeConfig( ], }, optimizeDeps: { - ...(userConfig.optimizeDeps ?? {}), + ...userOptimizeDeps, include: [ ...getDefaultOptimizeDeps(cwd), ...(hasDep("@agent-native/pinpoint", cwd) diff --git a/scripts/guard-env-documentation.ts b/scripts/guard-env-documentation.ts index 8a52c981e1b..2509d9444c4 100644 --- a/scripts/guard-env-documentation.ts +++ b/scripts/guard-env-documentation.ts @@ -30,6 +30,7 @@ const PUBLIC_EXACT_KEYS = new Set([ "AUTH_DISABLED", "AUTH_MAGIC_LINK", "AUTH_MODE", + "AUTH_REQUIRE_EMAIL_VERIFICATION", "AUTH_SKIP_EMAIL_VERIFICATION", "BRAVE_SEARCH_API_KEY", "BETTER_AUTH_SECRET",