Skip to content

Conversation

@ThisIsMissEm
Copy link
Contributor

@ThisIsMissEm ThisIsMissEm commented Aug 19, 2025

Description

This implements the often requested (based on the discussion forum) feature of being able to disable email login/registration and being able to allow email login but prevent registration via environment variable (previously it was only possible to disable this through the admin panel)

Discussions:

The logic here is:

  • email login is enabled by default (the .default("true") in env.ts)
  • if email login is disabled, then email registration is also disabled (and this cannot be overridden in the Admin)
  • if both email login and alternative login forms are not enabled, then a misconfiguration error is shown.
  • if email login is enabled, then email registration can be configured by environment variable, if this is the case, then the Admin UI toggle for email registration is disabled and the value displayed is the value of the environment variable.

Notes

  • When developing I needed to have .env in the root of the repo symlinked to apps/web/.env for next.js to pick it up for some reason, when running pnpm dev from the root of the project — I'm wondering if turbo is doing something that confuses node's process.cwd() such that the .env file doesn't load from the root of the repo, but apps/web instead.

Checklist

Please check off all the following items with an "x" in the boxes before requesting a review.

  • I have performed a self-review of my code
  • My code follows the code style of this project
  • I have commented my code, particularly in hard-to-understand areas

Screenshots

With OIDC enabled and Email login enabled:
image

With OIDC Enabled and Email registration disabled:
image

With OIDC Enabled and Email login disabled:
image

With neither OIDC or Email login enabled:
image

The admin panel if email registration is disabled (or with email login disabled):
image

Testing

Whilst I've done extensive manual testing to ensure this all works, I'm not sure how we'd approach automated tests for this.

Summary by CodeRabbit

  • New Features

    • Added environment toggles for email login and user registration; UI hides email login and shows a “Login is currently not configured.” message when no alternatives exist.
    • Registration and “Sign up” visibility across pages now respect the new toggles.
    • Control Panel surfaces when registration is governed by environment variables.
    • Authentication only enables email-based login when the toggle is on.
    • Added translations for related messages.
  • Documentation

    • Documented EMAIL_LOGIN_ENABLED and REGISTRATION_ENABLED.

@CLAassistant
Copy link

CLAassistant commented Aug 19, 2025

CLA assistant check
All committers have signed the CLA.

@vercel
Copy link

vercel bot commented Aug 19, 2025

@ThisIsMissEm is attempting to deploy a commit to the rallly Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 19, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Warning

Rate limit exceeded

@lukevella has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 10 minutes and 42 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 61a0745 and c31fab3.

📒 Files selected for processing (1)
  • apps/web/src/auth/providers/email.ts (1 hunks)

Walkthrough

Adds environment-controlled feature flags for email login and registration, exposing two env vars and feature flags, gating the email auth provider and related UI, replacing instance-settings-based registration checks with a server helper, updating NextAuth provider wiring, i18n, and docs.

Changes

Cohort / File(s) Summary
Docs
apps/docs/self-hosting/configuration.mdx
Adds documentation for EMAIL_LOGIN_ENABLED and REGISTRATION_ENABLED environment variables under Auth; explains defaults and effects; no code changes.
i18n
apps/web/public/locales/en/app.json
Adds keys configuredByEnvironmentVariable and loginNotConfigured.
Environment schema & runtime
apps/web/src/env.ts
Adds EMAIL_LOGIN_ENABLED and REGISTRATION_ENABLED to server env schema and runtime mapping.
Feature flags config/types
apps/web/src/lib/feature-flags/config.ts, apps/web/src/lib/feature-flags/types.ts
Adds emailLogin and registration flags; registration flag computed as emailLogin && registration.
Email provider gating
apps/web/src/auth/providers/email.ts
Converts EmailProvider to a factory that returns null when emailLogin feature is disabled; otherwise returns existing provider config and preserves sendVerificationRequest flow.
NextAuth wiring
apps/web/src/next-auth.ts
Consolidates providers into a single array, includes EmailProvider() conditionally, filters falsy providers; preserves overall auth flow.
Registration resolution utility
apps/web/src/utils/get-registration-enabled.ts
New server-only helper getRegistrationEnabled() that checks the registration feature flag then instance settings to determine whether registration is allowed.
Auth pages (login/register/new)
apps/web/src/app/[locale]/(auth)/login/page.tsx, apps/web/src/app/[locale]/(auth)/register/page.tsx, apps/web/src/app/[locale]/(optional-space)/new/page.tsx
Replace instance-settings checks with getRegistrationEnabled(); gate email form/UI on emailLogin feature; adjust header/description and import changes.
Instance settings form UI
apps/web/src/app/[locale]/control-panel/settings/instance-settings-form.tsx
Adds useFeatureFlag("registration") usage, disables registration toggle when env-controlled, shows alert using configuredByEnvironmentVariable i18n key.
TRPC auth router
apps/web/src/trpc/routers/auth.ts
Replaces instance-settings-based registration gate with getRegistrationEnabled() and throws when registration is disabled.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor U as User
  participant LP as Login Page
  participant FF as FeatureFlags (isFeatureEnabled)
  participant REG as getRegistrationEnabled
  participant IS as InstanceSettings
  participant NA as NextAuth

  U->>LP: Visit /login
  LP->>FF: isFeatureEnabled("emailLogin")
  alt emailLogin = true
    LP-->>U: Render email login form
    LP->>NA: Initialize providers (includes EmailProvider())
  else emailLogin = false
    LP-->>U: Hide email form
    LP-->>U: If no other providers, show "loginNotConfigured"
  end

  U->>LP: See sign-up option?
  LP->>REG: getRegistrationEnabled()
  REG->>FF: isFeatureEnabled("registration")
  alt registration flag = false
    REG-->>LP: false
  else registration flag = true
    REG->>IS: getInstanceSettings()
    IS-->>REG: { disableUserRegistration }
    REG-->>LP: !disableUserRegistration
  end
  LP-->>U: Render Sign up button if true
Loading
sequenceDiagram
  autonumber
  participant NA as NextAuth init
  participant FF as FeatureFlags
  participant EP as EmailProvider()
  participant GP as OtherProviders

  NA->>EP: Call EmailProvider()
  EP->>FF: isFeatureEnabled("emailLogin")
  alt enabled
    EP-->>NA: provider object
  else disabled
    EP-->>NA: null
  end
  NA->>GP: Initialize Google/Microsoft/OIDC
  NA->>NA: providers = [Guest, ...[EP, GP].filter(Boolean)]
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

A rabbit flips a feature flag, hop!
Email links may pause or pop,
Gates for signup rise and fall,
Env whispers rules to us all.
Thump-thump—toggles set, we ship the lot! 🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

@ThisIsMissEm
Copy link
Contributor Author

ThisIsMissEm commented Aug 19, 2025

@lukevella this is the first of two PRs I'm working on as part of deploying rallly for https://nivenly.org

The main thing we noticed was that with the licensing, we couldn't limit registration or email login via environment variables, and additionally we couldn't gate OIDC access by scope (this is a bit more complex), but both of these things made it hard for us to actually enforce the license requirements.

@lukevella
Copy link
Owner

Hi @ThisIsMissEm, can you help me understand how these changes help your organization? Why is disabling the registration page with an environment variable preferred over using the Control Panel? Who benefits from removing the option to login with a OTP/magic link?

@ThisIsMissEm
Copy link
Contributor Author

ThisIsMissEm commented Aug 20, 2025

@lukevella we generally prefer environment variables for this sort of configuration as it reduces the need to manually go in and configure something after you deploy it.

We're wanting to use exclusively SSO for accessing rallly, adding email magic link on top of SSO accounts sidesteps the SSO functionality.

That is, the SSO provider handles all the account security requirements, so magic link just isn't useful for us, and in fact downgrades our security.

@lukevella lukevella requested a review from Copilot August 20, 2025 15:35

This comment was marked as outdated.

@lukevella lukevella force-pushed the main branch 2 times, most recently from 2c3286e to cf39823 Compare August 21, 2025 20:17
@lukevella lukevella force-pushed the feat/configure-email-auth-by-environment-variables branch from 47b1248 to 3dc7bba Compare September 5, 2025 11:13
@lukevella lukevella requested a review from Copilot September 5, 2025 11:18
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR implements environment variable control for email authentication settings, allowing administrators to disable email login/registration independently through configuration.

  • Adds EMAIL_LOGIN_ENABLED and REGISTRATION_ENABLED environment variables with default values of "true"
  • Updates authentication flow to conditionally show email login forms and prevent registration when disabled
  • Modifies admin panel to display locked state when registration is controlled by environment variables

Reviewed Changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
apps/web/src/env.ts Defines new environment variables for email login and registration control
apps/web/src/lib/feature-flags/config.ts Integrates environment variables into feature flag system
apps/web/src/auth/providers/email.ts Makes email provider conditional based on feature flag
apps/web/src/utils/get-registration-enabled.ts New utility to check if registration is enabled considering both feature flags and instance settings
apps/web/src/app/[locale]/(auth)/login/page.tsx Updates login page to conditionally show email form and display configuration messages
apps/web/src/app/[locale]/control-panel/settings/instance-settings-form.tsx Adds UI to show when registration is controlled by environment variable
apps/docs/self-hosting/configuration.mdx Documents the new environment variables

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@vercel
Copy link

vercel bot commented Sep 5, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
app Ready Ready Preview Comment Sep 9, 2025 6:38pm
landing Ready Ready Preview Comment Sep 9, 2025 6:38pm

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/src/trpc/routers/auth.ts (1)

60-65: Add client‐side handling for disabled registration
The registration UI currently doesn’t catch the User registration is disabled TRPCError. Wrap your requestRegistration call in a try/catch (or error boundary), detect the BAD_REQUEST code or message, map it to the new i18n key (e.g., registration.disabled), and display a localized, user‐friendly message instead of the raw server string.

♻️ Duplicate comments (1)
apps/web/src/auth/providers/email.ts (1)

9-12: Provider gating via feature flag — good; returns null explicitly

Returning null when disabled matches the factory pattern and avoids ambiguous undefined.

🧹 Nitpick comments (9)
apps/web/src/trpc/routers/auth.ts (1)

60-65: Prefer FORBIDDEN and a machine-readable reason (or localization)

400 isn’t ideal for a disabled capability. Use 403 and a stable reason code (client can localize), or fetch a translation on the server.

-        const isRegistrationEnabled = await getRegistrationEnabled();
-        if (!isRegistrationEnabled) {
-          throw new TRPCError({
-            code: "BAD_REQUEST",
-            message: "User registration is disabled",
-          });
-        }
+        const isRegistrationEnabled = await getRegistrationEnabled();
+        if (!isRegistrationEnabled) {
+          throw new TRPCError({
+            code: "FORBIDDEN",
+            message: "registrationDisabled",
+          });
+        }
apps/web/public/locales/en/app.json (1)

14-14: Wording nit

More idiomatic phrasing.

-  "configuredByEnvironmentVariable": "This setting has been configured by environment variable.",
+  "configuredByEnvironmentVariable": "This setting is configured via an environment variable.",
apps/web/src/next-auth.ts (1)

36-41: Use a typed filter instead of assertion

Narrow the type with a user-defined type guard and drop as Provider[].

-    ...([
-      EmailProvider(),
-      GoogleProvider(),
-      OIDCProvider(),
-      MicrosoftProvider(),
-    ].filter(Boolean) as Provider[]),
+    ...(
+      [EmailProvider(), GoogleProvider(), OIDCProvider(), MicrosoftProvider()].filter(
+        (p): p is Provider => Boolean(p),
+      )
+    ),
apps/web/src/app/[locale]/control-panel/settings/instance-settings-form.tsx (1)

21-21: Wrap icons with to match design system

Per guidelines, wrap lucide icons with @rallly/ui Icon for sizing/color.

-import { ContainerIcon } from "lucide-react";
+import { ContainerIcon } from "lucide-react";
+import { Icon } from "@rallly/ui/icon";
-              <Alert variant="note" className="mt-4">
-                <ContainerIcon />
+              <Alert variant="note" className="mt-4">
+                <Icon>
+                  <ContainerIcon />
+                </Icon>

Also applies to: 123-134

apps/web/src/env.ts (1)

53-55: Optional: coerce to boolean at the schema to avoid string compares downstream

You can transform these to booleans in env.ts and drop === "true" checks elsewhere.

-    EMAIL_LOGIN_ENABLED: z.enum(["true", "false"]).default("true"),
-    REGISTRATION_ENABLED: z.enum(["true", "false"]).default("true"),
+    EMAIL_LOGIN_ENABLED: z
+      .enum(["true", "false"])
+      .default("true")
+      .transform((v) => v === "true"),
+    REGISTRATION_ENABLED: z
+      .enum(["true", "false"])
+      .default("true")
+      .transform((v) => v === "true"),

If you do this, update usages to treat env.EMAIL_LOGIN_ENABLED/REGISTRATION_ENABLED as boolean.

Also applies to: 121-123

apps/web/src/utils/get-registration-enabled.ts (1)

6-13: Add explicit return type and basic tests (optional)

Minor clarity: declare return type. Also worth a small unit test matrix for env/settings combos.

-export async function getRegistrationEnabled() {
+export async function getRegistrationEnabled(): Promise<boolean> {

I can add tests covering:

  • emailLogin=false ⇒ false
  • emailLogin=true + registration=false ⇒ false
  • both true + instanceSettings.disableUserRegistration=false ⇒ true
  • both true + instanceSettings.disableUserRegistration=true ⇒ false
apps/web/src/app/[locale]/(auth)/register/page.tsx (1)

21-26: Parallelize translation and registration checks to shave TTFB

Fetch both in parallel.

Apply:

-  const { t } = await getTranslation(params.locale);
-  const isRegistrationEnabled = await getRegistrationEnabled();
+  const [{ t }, isRegistrationEnabled] = await Promise.all([
+    getTranslation(params.locale),
+    getRegistrationEnabled(),
+  ]);
apps/web/src/auth/providers/email.ts (1)

9-12: Add explicit return type for the provider factory

Improves clarity for call sites and avoids accidental misuse.

Apply:

-export const EmailProvider = () => {
+export const EmailProvider = ():
+  | ReturnType<typeof NodemailerProvider>
+  | null => {
apps/web/src/app/[locale]/(auth)/login/page.tsx (1)

87-100: Minor: avoid rendering an empty providers container

Guard on length to skip an empty grid.

Apply:

-        {socialProviders ? (
+        {socialProviders.length > 0 ? (
           <div className="grid gap-4">
             {socialProviders.map((provider) =>
               provider ? (
                 <SSOProvider
                   key={provider.id}
                   providerId={provider.id}
                   name={provider.options?.name || provider.name}
                   redirectTo={searchParams?.redirectTo}
                 />
               ) : null,
             )}
           </div>
         ) : null}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between b24b584 and 083d9a3.

📒 Files selected for processing (13)
  • apps/docs/self-hosting/configuration.mdx (1 hunks)
  • apps/web/public/locales/en/app.json (2 hunks)
  • apps/web/src/app/[locale]/(auth)/login/page.tsx (5 hunks)
  • apps/web/src/app/[locale]/(auth)/register/page.tsx (2 hunks)
  • apps/web/src/app/[locale]/(optional-space)/new/page.tsx (2 hunks)
  • apps/web/src/app/[locale]/control-panel/settings/instance-settings-form.tsx (6 hunks)
  • apps/web/src/auth/providers/email.ts (1 hunks)
  • apps/web/src/env.ts (2 hunks)
  • apps/web/src/lib/feature-flags/config.ts (1 hunks)
  • apps/web/src/lib/feature-flags/types.ts (1 hunks)
  • apps/web/src/next-auth.ts (1 hunks)
  • apps/web/src/trpc/routers/auth.ts (2 hunks)
  • apps/web/src/utils/get-registration-enabled.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (12)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.windsurfrules)

**/*.{js,jsx,ts,tsx}: Use dayjs for date handling
Use tailwindcss for styling
Use react-query for data fetching
Use react-hook-form for form handling
Prefer implicit return values over explicit return values
Use zod for form validation
Prefer double quotes for strings over single quotes
Only add comments when it is necessary to explain code that isn't self-explanatory

Files:

  • apps/web/src/utils/get-registration-enabled.ts
  • apps/web/src/lib/feature-flags/types.ts
  • apps/web/src/app/[locale]/(optional-space)/new/page.tsx
  • apps/web/src/trpc/routers/auth.ts
  • apps/web/src/env.ts
  • apps/web/src/lib/feature-flags/config.ts
  • apps/web/src/app/[locale]/(auth)/login/page.tsx
  • apps/web/src/app/[locale]/(auth)/register/page.tsx
  • apps/web/src/next-auth.ts
  • apps/web/src/auth/providers/email.ts
  • apps/web/src/app/[locale]/control-panel/settings/instance-settings-form.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.windsurfrules)

Create separate import statements for types

Files:

  • apps/web/src/utils/get-registration-enabled.ts
  • apps/web/src/lib/feature-flags/types.ts
  • apps/web/src/app/[locale]/(optional-space)/new/page.tsx
  • apps/web/src/trpc/routers/auth.ts
  • apps/web/src/env.ts
  • apps/web/src/lib/feature-flags/config.ts
  • apps/web/src/app/[locale]/(auth)/login/page.tsx
  • apps/web/src/app/[locale]/(auth)/register/page.tsx
  • apps/web/src/next-auth.ts
  • apps/web/src/auth/providers/email.ts
  • apps/web/src/app/[locale]/control-panel/settings/instance-settings-form.tsx
**/*.ts

📄 CodeRabbit inference engine (.windsurfrules)

On the server use the getTranslations function from @/i18n/server to get the translations.

Files:

  • apps/web/src/utils/get-registration-enabled.ts
  • apps/web/src/lib/feature-flags/types.ts
  • apps/web/src/trpc/routers/auth.ts
  • apps/web/src/env.ts
  • apps/web/src/lib/feature-flags/config.ts
  • apps/web/src/next-auth.ts
  • apps/web/src/auth/providers/email.ts
**/*

📄 CodeRabbit inference engine (.windsurfrules)

Always use kebab-case for file names

Files:

  • apps/web/src/utils/get-registration-enabled.ts
  • apps/web/public/locales/en/app.json
  • apps/web/src/lib/feature-flags/types.ts
  • apps/web/src/app/[locale]/(optional-space)/new/page.tsx
  • apps/docs/self-hosting/configuration.mdx
  • apps/web/src/trpc/routers/auth.ts
  • apps/web/src/env.ts
  • apps/web/src/lib/feature-flags/config.ts
  • apps/web/src/app/[locale]/(auth)/login/page.tsx
  • apps/web/src/app/[locale]/(auth)/register/page.tsx
  • apps/web/src/next-auth.ts
  • apps/web/src/auth/providers/email.ts
  • apps/web/src/app/[locale]/control-panel/settings/instance-settings-form.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use Biome for code formatting with 2-space indentation and double quotes

Files:

  • apps/web/src/utils/get-registration-enabled.ts
  • apps/web/src/lib/feature-flags/types.ts
  • apps/web/src/app/[locale]/(optional-space)/new/page.tsx
  • apps/web/src/trpc/routers/auth.ts
  • apps/web/src/env.ts
  • apps/web/src/lib/feature-flags/config.ts
  • apps/web/src/app/[locale]/(auth)/login/page.tsx
  • apps/web/src/app/[locale]/(auth)/register/page.tsx
  • apps/web/src/next-auth.ts
  • apps/web/src/auth/providers/email.ts
  • apps/web/src/app/[locale]/control-panel/settings/instance-settings-form.tsx
apps/web/src/**/*.{tsx,ts}

📄 CodeRabbit inference engine (CLAUDE.md)

Use the useDialog hook from @rallly/ui/dialog to manage dialog state instead of manual useState

Files:

  • apps/web/src/utils/get-registration-enabled.ts
  • apps/web/src/lib/feature-flags/types.ts
  • apps/web/src/app/[locale]/(optional-space)/new/page.tsx
  • apps/web/src/trpc/routers/auth.ts
  • apps/web/src/env.ts
  • apps/web/src/lib/feature-flags/config.ts
  • apps/web/src/app/[locale]/(auth)/login/page.tsx
  • apps/web/src/app/[locale]/(auth)/register/page.tsx
  • apps/web/src/next-auth.ts
  • apps/web/src/auth/providers/email.ts
  • apps/web/src/app/[locale]/control-panel/settings/instance-settings-form.tsx
apps/web/src/lib/feature-flags/**

📄 CodeRabbit inference engine (CLAUDE.md)

Store feature flag logic under apps/web/src/lib/feature-flags/

Files:

  • apps/web/src/lib/feature-flags/types.ts
  • apps/web/src/lib/feature-flags/config.ts
**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.windsurfrules)

**/*.{jsx,tsx}: All text in the UI should be translated using either the Trans component or the useTranslation hook
Prefer composable components in the style of shadcn UI over large monolithic components
DropdownMenuItem is a flex container with a preset gap so there is no need to add margins to the children
The size and colour of an icon should be set by wrapping it with the component from @rallly/ui/icon which will give it the correct colour and size.
Keep the props of a component as minimal as possible. Pass only the bare minimum amount of information needed to it.
All text in the UI should be translatable.
Always use a composable patterns when building components
Use cn() from @rallly/ui to compose classes
Prefer using the React module APIs (e.g. React.useState) instead of standalone hooks (e.g. useState)

Files:

  • apps/web/src/app/[locale]/(optional-space)/new/page.tsx
  • apps/web/src/app/[locale]/(auth)/login/page.tsx
  • apps/web/src/app/[locale]/(auth)/register/page.tsx
  • apps/web/src/app/[locale]/control-panel/settings/instance-settings-form.tsx
**/*.tsx

📄 CodeRabbit inference engine (.windsurfrules)

Add the "use client" directive to the top of any .tsx file that requires client-side javascript

**/*.tsx: Prefer inline prop types for simple React component props instead of named interfaces
Only create named interfaces for component props when the props are reused or complex

Files:

  • apps/web/src/app/[locale]/(optional-space)/new/page.tsx
  • apps/web/src/app/[locale]/(auth)/login/page.tsx
  • apps/web/src/app/[locale]/(auth)/register/page.tsx
  • apps/web/src/app/[locale]/control-panel/settings/instance-settings-form.tsx
apps/web/src/app/**

📄 CodeRabbit inference engine (CLAUDE.md)

Ensure route handlers follow Next.js App Router conventions

Files:

  • apps/web/src/app/[locale]/(optional-space)/new/page.tsx
  • apps/web/src/app/[locale]/(auth)/login/page.tsx
  • apps/web/src/app/[locale]/(auth)/register/page.tsx
  • apps/web/src/app/[locale]/control-panel/settings/instance-settings-form.tsx
apps/web/src/trpc/routers/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place tRPC routers under apps/web/src/trpc/routers/

Files:

  • apps/web/src/trpc/routers/auth.ts
apps/web/src/auth/**

📄 CodeRabbit inference engine (CLAUDE.md)

Keep NextAuth.js authentication setup under apps/web/src/auth/

Files:

  • apps/web/src/auth/providers/email.ts
🧠 Learnings (2)
📚 Learning: 2025-08-18T17:39:22.684Z
Learnt from: CR
PR: lukevella/rallly#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-18T17:39:22.684Z
Learning: Applies to apps/web/src/lib/feature-flags/** : Store feature flag logic under apps/web/src/lib/feature-flags/

Applied to files:

  • apps/web/src/lib/feature-flags/config.ts
📚 Learning: 2025-08-18T17:39:22.684Z
Learnt from: CR
PR: lukevella/rallly#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-18T17:39:22.684Z
Learning: Applies to apps/web/src/auth/** : Keep NextAuth.js authentication setup under apps/web/src/auth/

Applied to files:

  • apps/web/src/next-auth.ts
🧬 Code graph analysis (9)
apps/web/src/utils/get-registration-enabled.ts (2)
apps/web/src/lib/feature-flags/server.ts (1)
  • isFeatureEnabled (5-7)
apps/web/src/features/instance-settings/queries.ts (1)
  • getInstanceSettings (7-27)
apps/web/src/app/[locale]/(optional-space)/new/page.tsx (2)
apps/web/src/next-auth.ts (1)
  • getLoggedIn (195-198)
apps/web/src/utils/get-registration-enabled.ts (1)
  • getRegistrationEnabled (6-13)
apps/web/src/trpc/routers/auth.ts (1)
apps/web/src/utils/get-registration-enabled.ts (1)
  • getRegistrationEnabled (6-13)
apps/web/src/lib/feature-flags/config.ts (5)
apps/web/src/env.ts (1)
  • env (4-143)
apps/web/src/lib/feature-flags/types.ts (1)
  • FeatureFlagConfig (1-7)
apps/web/src/lib/storage/constants.ts (1)
  • isStorageEnabled (3-5)
apps/web/src/features/billing/constants.ts (1)
  • isBillingEnabled (3-3)
apps/web/src/features/feedback/constants.ts (1)
  • isFeedbackEnabled (3-3)
apps/web/src/app/[locale]/(auth)/login/page.tsx (5)
apps/web/src/utils/get-registration-enabled.ts (1)
  • getRegistrationEnabled (6-13)
apps/web/src/i18n/server.ts (1)
  • getTranslation (8-30)
apps/web/src/lib/feature-flags/server.ts (1)
  • isFeatureEnabled (5-7)
apps/web/src/app/[locale]/(auth)/login/components/login-email-form.tsx (1)
  • LoginWithEmailForm (34-127)
apps/web/src/app/[locale]/(auth)/login/components/or-divider.tsx (1)
  • OrDivider (3-12)
apps/web/src/app/[locale]/(auth)/register/page.tsx (1)
apps/web/src/utils/get-registration-enabled.ts (1)
  • getRegistrationEnabled (6-13)
apps/web/src/next-auth.ts (4)
apps/web/src/auth/providers/email.ts (1)
  • EmailProvider (9-49)
apps/web/src/auth/providers/google.ts (1)
  • GoogleProvider (3-11)
apps/web/src/auth/providers/oidc.ts (1)
  • OIDCProvider (7-41)
apps/web/src/auth/providers/microsoft.ts (1)
  • MicrosoftProvider (3-12)
apps/web/src/auth/providers/email.ts (3)
apps/web/src/lib/feature-flags/server.ts (1)
  • isFeatureEnabled (5-7)
apps/web/src/utils/emails.ts (1)
  • getEmailClient (9-34)
packages/utils/src/absolute-url.ts (1)
  • absoluteUrl (18-38)
apps/web/src/app/[locale]/control-panel/settings/instance-settings-form.tsx (1)
apps/web/src/lib/feature-flags/client.tsx (1)
  • useFeatureFlag (26-34)
🔇 Additional comments (17)
apps/web/src/lib/feature-flags/types.ts (1)

5-6: LGTM: new flags fit the model

Adding emailLogin and registration to FeatureFlagConfig cleanly extends Feature = keyof FeatureFlagConfig. No issues spotted.

apps/web/src/trpc/routers/auth.ts (1)

16-16: LGTM: centralizing registration gate

Switching to getRegistrationEnabled() keeps the router lean and consistent with feature flags.

apps/web/public/locales/en/app.json (1)

41-41: LGTM: concise empty-state copy

Text is clear and neutral.

apps/docs/self-hosting/configuration.mdx (1)

68-79: Clarify registration applies only to email-based flows, not SSO
Confirmed via code search that REGISTRATION_ENABLED isn’t enforced in NextAuth/OAuth flows and only disables email-based registration; update the ParamField descriptions in apps/docs/self-hosting/configuration.mdx accordingly per the suggested diff.

apps/web/src/app/[locale]/(optional-space)/new/page.tsx (2)

11-18: Good switch to server-validated registration gating

Using getRegistrationEnabled() with Promise.all keeps UI in sync with server rules and caches via getInstanceSettings(). Looks solid.


49-58: Sign-up button correctly gated

Rendering Sign up only when isRegistrationEnabled prevents dead links to /register when registration is off.

apps/web/src/app/[locale]/control-panel/settings/instance-settings-form.tsx (2)

123-123: Confirm Alert variant

Is "note" a supported Alert variant in @rallly/ui/alert? If not, switch to "info" to avoid styling fallback.


49-49: Client feature-flag usage looks right

useFeatureFlag("registration") cleanly reflects env-driven gating in the UI.

apps/web/src/env.ts (1)

53-55: Env flags added with sane defaults

EMAIL_LOGIN_ENABLED and REGISTRATION_ENABLED default to "true" and are server-only. Looks good.

apps/web/src/lib/feature-flags/config.ts (1)

7-16: Flag derivation is consistent with PR intent

emailLogin mirrors EMAIL_LOGIN_ENABLED; registration requires both email login and registration env flags. Matches “registration disabled when email login is off.”

apps/web/src/utils/get-registration-enabled.ts (1)

6-13: Server-side source of truth for registration is clean

Early-return on feature flag and fallback to instance settings is straightforward and cache-friendly via getInstanceSettings().

apps/web/src/app/[locale]/(auth)/register/page.tsx (1)

5-5: Centralized registration gating via server utility — LGTM

Import and usage of getRegistrationEnabled with a clear 404 guard reads well and aligns with the new feature-flag flow.

Also applies to: 22-22, 24-26

apps/web/src/auth/providers/email.ts (1)

9-12: Resolved: .filter(Boolean) already drops null providers

apps/web/src/app/[locale]/(auth)/login/page.tsx (4)

26-34: Good: concurrent fetch of translation and registration flag

Promise.all here is appropriate.


44-46: Server-side gate for email login — LGTM

Local isEmailLoginEnabled via isFeatureEnabled keeps render logic simple.


79-81: Divider gating is correct

Only shows when both email and another provider are present.


61-75: Add missing loginNotConfigured key to all non-English locale files
All app.json files under apps/web/public/locales/* except en/app.json are missing the loginNotConfigured entry—add appropriate translations to prevent fallback to the default string.

⛔ Skipped due to learnings
Learnt from: CR
PR: lukevella/rallly#0
File: .windsurfrules:0-0
Timestamp: 2025-07-21T13:12:34.705Z
Learning: Applies to **/i18n/**/*.json : i18n keys are in camelCase.
Learnt from: CR
PR: lukevella/rallly#0
File: .windsurfrules:0-0
Timestamp: 2025-07-21T13:12:34.705Z
Learning: Applies to **/i18n/**/*.json : Never manually add translations to .json files. This will be handled by our tooling.
Learnt from: CR
PR: lukevella/rallly#0
File: .windsurfrules:0-0
Timestamp: 2025-07-21T13:12:34.705Z
Learning: Applies to **/*.{jsx,tsx} : All text in the UI should be translatable.

@lukevella lukevella force-pushed the feat/configure-email-auth-by-environment-variables branch from 8f6ef00 to 61a0745 Compare September 5, 2025 13:30
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
apps/web/src/auth/providers/email.ts (1)

22-47: Non-existent user yields silent success; throw to fail sign-in.

If user is not found, NextAuth treats the request as ok and shows “check your email” even though nothing is sent. Throw to surface failure.

Apply:

     async sendVerificationRequest({ identifier: email, token, url }) {
       const user = await prisma.user.findUnique({
         where: {
           email,
         },
         select: {
           name: true,
           locale: true,
         },
       });

-      if (user) {
-        await getEmailClient(user.locale ?? undefined).sendTemplate(
-          "LoginEmail",
-          {
-            to: email,
-            props: {
-              magicLink: absoluteUrl("/auth/login", {
-                magicLink: url,
-              }),
-              code: token,
-            },
-          },
-        );
-      }
+      if (!user) {
+        throw new Error("USER_NOT_FOUND");
+      }
+
+      await getEmailClient(user.locale ?? undefined).sendTemplate("LoginEmail", {
+        to: email,
+        props: {
+          magicLink: absoluteUrl("/auth/login", { magicLink: url }),
+          code: token,
+        },
+      });
     },
🧹 Nitpick comments (5)
apps/web/src/auth/providers/email.ts (2)

14-21: Add an explicit return type for EmailProvider to aid inference and DX.

-export const EmailProvider = () => {
+export const EmailProvider = ():
+  | ReturnType<typeof NodemailerProvider>
+  | null => {

16-16: Prefer validated env over process.env for sender address.

Keeps env usage consistent with getEmailClient and env.ts validation.

-    from: process.env.NOREPLY_EMAIL,
+    from: env.NOREPLY_EMAIL,

Add import:

+import { env } from "@/env";
apps/web/src/trpc/routers/auth.ts (1)

60-66: Consider using FORBIDDEN and a translatable reason code.

BAD_REQUEST works, but FORBIDDEN better fits policy denial. Also prefer a machine-readable reason to localize on the client.

-          throw new TRPCError({
-            code: "BAD_REQUEST",
-            message: "User registration is disabled",
-          });
+          throw new TRPCError({
+            code: "FORBIDDEN",
+            message: "registrationDisabled",
+          });
apps/web/src/utils/get-registration-enabled.ts (1)

6-13: Solid central gate; add explicit return type for clarity.

-export async function getRegistrationEnabled() {
+export async function getRegistrationEnabled(): Promise<boolean> {
apps/web/src/app/[locale]/(auth)/register/page.tsx (1)

22-26: 404 on disabled registration: consider a dedicated UX.

Optional: instead of notFound(), render a localized “Registration disabled” page or redirect to /login with a query param so the UI can show a friendly message.

-  if (!isRegistrationEnabled) {
-    return notFound();
-  }
+  if (!isRegistrationEnabled) {
+    redirect("/login?registrationDisabled=1");
+  }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 083d9a3 and 61a0745.

📒 Files selected for processing (13)
  • apps/docs/self-hosting/configuration.mdx (1 hunks)
  • apps/web/public/locales/en/app.json (2 hunks)
  • apps/web/src/app/[locale]/(auth)/login/page.tsx (5 hunks)
  • apps/web/src/app/[locale]/(auth)/register/page.tsx (2 hunks)
  • apps/web/src/app/[locale]/(optional-space)/new/page.tsx (2 hunks)
  • apps/web/src/app/[locale]/control-panel/settings/instance-settings-form.tsx (6 hunks)
  • apps/web/src/auth/providers/email.ts (1 hunks)
  • apps/web/src/env.ts (2 hunks)
  • apps/web/src/lib/feature-flags/config.ts (1 hunks)
  • apps/web/src/lib/feature-flags/types.ts (1 hunks)
  • apps/web/src/next-auth.ts (1 hunks)
  • apps/web/src/trpc/routers/auth.ts (2 hunks)
  • apps/web/src/utils/get-registration-enabled.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (8)
  • apps/web/src/env.ts
  • apps/web/src/lib/feature-flags/config.ts
  • apps/web/public/locales/en/app.json
  • apps/web/src/app/[locale]/control-panel/settings/instance-settings-form.tsx
  • apps/web/src/lib/feature-flags/types.ts
  • apps/docs/self-hosting/configuration.mdx
  • apps/web/src/next-auth.ts
  • apps/web/src/app/[locale]/(auth)/login/page.tsx
🧰 Additional context used
📓 Path-based instructions (11)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.windsurfrules)

**/*.{js,jsx,ts,tsx}: Use dayjs for date handling
Use tailwindcss for styling
Use react-query for data fetching
Use react-hook-form for form handling
Prefer implicit return values over explicit return values
Use zod for form validation
Prefer double quotes for strings over single quotes
Only add comments when it is necessary to explain code that isn't self-explanatory

Files:

  • apps/web/src/utils/get-registration-enabled.ts
  • apps/web/src/app/[locale]/(optional-space)/new/page.tsx
  • apps/web/src/app/[locale]/(auth)/register/page.tsx
  • apps/web/src/auth/providers/email.ts
  • apps/web/src/trpc/routers/auth.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.windsurfrules)

Create separate import statements for types

Files:

  • apps/web/src/utils/get-registration-enabled.ts
  • apps/web/src/app/[locale]/(optional-space)/new/page.tsx
  • apps/web/src/app/[locale]/(auth)/register/page.tsx
  • apps/web/src/auth/providers/email.ts
  • apps/web/src/trpc/routers/auth.ts
**/*.ts

📄 CodeRabbit inference engine (.windsurfrules)

On the server use the getTranslations function from @/i18n/server to get the translations.

Files:

  • apps/web/src/utils/get-registration-enabled.ts
  • apps/web/src/auth/providers/email.ts
  • apps/web/src/trpc/routers/auth.ts
**/*

📄 CodeRabbit inference engine (.windsurfrules)

Always use kebab-case for file names

Files:

  • apps/web/src/utils/get-registration-enabled.ts
  • apps/web/src/app/[locale]/(optional-space)/new/page.tsx
  • apps/web/src/app/[locale]/(auth)/register/page.tsx
  • apps/web/src/auth/providers/email.ts
  • apps/web/src/trpc/routers/auth.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use Biome for code formatting with 2-space indentation and double quotes

Files:

  • apps/web/src/utils/get-registration-enabled.ts
  • apps/web/src/app/[locale]/(optional-space)/new/page.tsx
  • apps/web/src/app/[locale]/(auth)/register/page.tsx
  • apps/web/src/auth/providers/email.ts
  • apps/web/src/trpc/routers/auth.ts
apps/web/src/**/*.{tsx,ts}

📄 CodeRabbit inference engine (CLAUDE.md)

Use the useDialog hook from @rallly/ui/dialog to manage dialog state instead of manual useState

Files:

  • apps/web/src/utils/get-registration-enabled.ts
  • apps/web/src/app/[locale]/(optional-space)/new/page.tsx
  • apps/web/src/app/[locale]/(auth)/register/page.tsx
  • apps/web/src/auth/providers/email.ts
  • apps/web/src/trpc/routers/auth.ts
**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.windsurfrules)

**/*.{jsx,tsx}: All text in the UI should be translated using either the Trans component or the useTranslation hook
Prefer composable components in the style of shadcn UI over large monolithic components
DropdownMenuItem is a flex container with a preset gap so there is no need to add margins to the children
The size and colour of an icon should be set by wrapping it with the component from @rallly/ui/icon which will give it the correct colour and size.
Keep the props of a component as minimal as possible. Pass only the bare minimum amount of information needed to it.
All text in the UI should be translatable.
Always use a composable patterns when building components
Use cn() from @rallly/ui to compose classes
Prefer using the React module APIs (e.g. React.useState) instead of standalone hooks (e.g. useState)

Files:

  • apps/web/src/app/[locale]/(optional-space)/new/page.tsx
  • apps/web/src/app/[locale]/(auth)/register/page.tsx
**/*.tsx

📄 CodeRabbit inference engine (.windsurfrules)

Add the "use client" directive to the top of any .tsx file that requires client-side javascript

**/*.tsx: Prefer inline prop types for simple React component props instead of named interfaces
Only create named interfaces for component props when the props are reused or complex

Files:

  • apps/web/src/app/[locale]/(optional-space)/new/page.tsx
  • apps/web/src/app/[locale]/(auth)/register/page.tsx
apps/web/src/app/**

📄 CodeRabbit inference engine (CLAUDE.md)

Ensure route handlers follow Next.js App Router conventions

Files:

  • apps/web/src/app/[locale]/(optional-space)/new/page.tsx
  • apps/web/src/app/[locale]/(auth)/register/page.tsx
apps/web/src/auth/**

📄 CodeRabbit inference engine (CLAUDE.md)

Keep NextAuth.js authentication setup under apps/web/src/auth/

Files:

  • apps/web/src/auth/providers/email.ts
apps/web/src/trpc/routers/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place tRPC routers under apps/web/src/trpc/routers/

Files:

  • apps/web/src/trpc/routers/auth.ts
🧬 Code graph analysis (5)
apps/web/src/utils/get-registration-enabled.ts (2)
apps/web/src/lib/feature-flags/server.ts (1)
  • isFeatureEnabled (5-7)
apps/web/src/features/instance-settings/queries.ts (1)
  • getInstanceSettings (7-27)
apps/web/src/app/[locale]/(optional-space)/new/page.tsx (2)
apps/web/src/next-auth.ts (1)
  • getLoggedIn (195-198)
apps/web/src/utils/get-registration-enabled.ts (1)
  • getRegistrationEnabled (6-13)
apps/web/src/app/[locale]/(auth)/register/page.tsx (1)
apps/web/src/utils/get-registration-enabled.ts (1)
  • getRegistrationEnabled (6-13)
apps/web/src/auth/providers/email.ts (4)
apps/web/src/lib/feature-flags/server.ts (1)
  • isFeatureEnabled (5-7)
packages/utils/src/nanoid.ts (1)
  • generateOtp (13-13)
apps/web/src/utils/emails.ts (1)
  • getEmailClient (9-34)
packages/utils/src/absolute-url.ts (1)
  • absoluteUrl (18-38)
apps/web/src/trpc/routers/auth.ts (1)
apps/web/src/utils/get-registration-enabled.ts (1)
  • getRegistrationEnabled (6-13)
🔇 Additional comments (6)
apps/web/src/auth/providers/email.ts (1)

10-12: Provider gating verified
EmailProvider returns null when the feature flag is off, and the NextAuth config applies .filter(Boolean) to its providers array, so falsy entries are correctly removed.

apps/web/src/trpc/routers/auth.ts (2)

60-66: Good: centralized registration gate via getRegistrationEnabled().


16-16: No changes needed. Registration is defined as isEmailLoginEnabled && isRegistrationEnabled (config.ts line 16), matching the intended composition so disabling email login also disables registration.

apps/web/src/app/[locale]/(optional-space)/new/page.tsx (2)

15-18: LGTM: parallelize fetch with Promise.all.


49-57: LGTM: signup CTA correctly gated by isRegistrationEnabled.

apps/web/src/app/[locale]/(auth)/register/page.tsx (1)

5-5: LGTM: uses shared getRegistrationEnabled helper; keeps logic consistent.

@vercel vercel bot temporarily deployed to Preview – landing September 5, 2025 16:11 Inactive
@lukevella lukevella changed the title Feature: Configure email auth by environment variables ✨ Configure email auth by environment variables Sep 10, 2025
@lukevella
Copy link
Owner

lukevella commented Sep 10, 2025

Hey @ThisIsMissEm, thanks for your help with this and sorry for the delay.

There was an issue that was causing our e2e tests to fail that was not easy to solve. It appears that importing the environment values from env.ts into the email provider was resulting in a circular dependency which is why I'm accessing the values directly from process.env instead for now.

I've made a few minor changes and renamed the env vars to EMAIL_LOGIN_ENABLED and REGISTRATION_ENABLED to be more consistent with other boolean env vars.

I'll merge this and will make a new release shortly.

Thank you for your help.

@lukevella lukevella merged commit f23f5f6 into lukevella:main Sep 10, 2025
10 checks passed
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