Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

♻️ Upgrade to next-auth v5 #1558

Merged
merged 9 commits into from
Feb 12, 2025
Merged

♻️ Upgrade to next-auth v5 #1558

merged 9 commits into from
Feb 12, 2025

Conversation

lukevella
Copy link
Owner

@lukevella lukevella commented Feb 12, 2025

Summary by CodeRabbit

  • New Features

    • Introduced additional sign-in options including Email, Guest, Microsoft, OIDC, and Registration Token providers.
    • Added a clear error message for invalid or expired magic link logins.
  • Bug Fixes

    • Improved redirection handling for smoother sign-in, account deletion, and verification flows.
    • Updated social login display to reflect the current provider naming.
  • Refactor

    • Standardized redirect parameter usage across authentication flows.
    • Enhanced session management through streamlined middleware and cookie migration.
  • Chores

    • Upgraded dependencies and removed deprecated configurations.
    • Updated tests to align with redirection parameter changes.
    • Removed obsolete environment variables and configurations.

Copy link

vercel bot commented Feb 12, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
app ✅ Ready (Inspect) Visit Preview 💬 Add feedback Feb 12, 2025 5:46am
1 Skipped Deployment
Name Status Preview Comments Updated (UTC)
landing ⬜️ Skipped (Inspect) Feb 12, 2025 5:46am

Copy link
Contributor

coderabbitai bot commented Feb 12, 2025

Walkthrough

This pull request introduces extensive updates across the codebase. It updates the i18next scanner configuration, upgrades and adds dependencies in package files, and revises various authentication flows. Multiple components now use a renamed redirection parameter (redirectTo instead of callbackUrl), and session retrieval has been shifted from getServerSession() to auth(). The PR also adds new authentication providers, integrates PostHog analytics into API routes, improves JWT handling for legacy tokens, and refactors middleware and adapter logic. Minor UI and environment variable changes are also included.

Changes

File(s) Change Summary
apps/web/i18next-scanner.config.js Updated exclusion pattern from !src/auth.ts to !src/next-auth*.ts.
apps/web/package.json Upgraded dependencies (@auth/prisma-adapter & next-auth) and added new ones (@panva/hkdf, jose).
package.json Added new field "dependencies": {}.
apps/web/public/locales/en/app.json Added new error message for magic link issues; repositioned "tooManyRequestsDescription".
apps/web/src/app/[locale]/(auth)/** (e.g., login-page.tsx, login-email-form.tsx, login-with-oidc.tsx, sso-provider.tsx, page.tsx, otp-form.tsx, register-link.tsx, login-link.tsx) Renamed parameter from callbackUrl to redirectTo in sign-in and redirection logic.
apps/web/src/app/api/** (e.g., auth/[...nextauth]/route.ts, notifications/unsubscribe/route.ts, stripe/checkout/route.ts, stripe/portal/route.ts, trpc/[trpc]/route.ts, user/verify-email-change/route.ts, trpc/server/create-ssr-helper.ts) Replaced getServerSession() with auth() for session retrieval in API endpoints and TRPC context.
apps/web/src/app/[locale]/(auth)/login/actions.ts Updated setVerificationEmail to set a cookie only when a user is found.
apps/web/src/auth/adapters/prisma.ts Removed the client parameter; now uses direct prisma import and changes type assertion to as Adapter.
apps/web/src/auth/providers/*.ts Introduced new authentication providers (Email, Google, Guest, Microsoft, OIDC, RegistrationToken) and removed legacy auth file.
apps/web/src/middleware.ts and apps/web/src/auth/middleware.ts Refactored middleware for authentication, simplifying logic and integrating PostHog tracking.
apps/web/src/auth/legacy/** Added JWT decoding functionality and cookie migration for legacy tokens using HKDF.
Environment files (.env.development, apps/web/.env.test) and scripts/docker-start.sh Removed NEXTAUTH_URL variable settings.
packages/posthog/src/server/index.ts Introduced a new withPosthog handler and removed the old posthogApiHandler function.
Miscellaneous (e.g., apps/web/src/components/poll/language-selector.tsx, apps/web/src/trpc/routers/auth.ts, type declaration files) Minor UI and type adjustments, including removal of button variant prop and updated import paths for helper functions.

Sequence Diagram(s)

sequenceDiagram
    participant U as User
    participant LP as LoginPage
    participant ML as MagicLinkFetch
    U->>LP: Request login via magic link
    LP->>ML: Validate magic link
    alt Invalid/Expired Link
        ML-->>LP: Response with error URL ("auth/error")
        LP->>LP: Set error state with translated message
    else Valid Link
        ML-->>LP: Response with valid URL
        LP->>U: Redirect using URL from redirectTo
    end
Loading
sequenceDiagram
    participant Req as Request
    participant MW as Middleware
    participant Auth as Auth Service
    Req->>MW: Incoming HTTP request
    MW->>Auth: Retrieve session via auth()
    Auth-->>MW: Return session data
    MW->>Req: Process request based on session
Loading

Possibly related PRs

  • ♻️ Simplify trpc context #1322: The changes in the main PR regarding the update of the callbackUrl to redirectTo in various components are directly related to the modifications in the retrieved PR, which also involves simplifying the context and handling of user sessions, including the use of redirectTo in the context of authentication.
  • ♻️ Upgrade to next-auth v5 #1547: The changes in the main PR, specifically the updates to the callbackUrl parameter to redirectTo in various components, are directly related to the changes in the retrieved PR, which also involves similar modifications to the same parameter across multiple files in the context of upgrading to next-auth v5.
  • ♻️ Clean up login page code #1525: The changes in the main PR, specifically the updates to the callbackUrl parameter in various components, are directly related to the modifications in the retrieved PR, which also involves changes to the handling of OAuth providers and the callbackUrl parameter in the login functionality. Both PRs focus on improving the login process and handling redirection parameters consistently.

Poem

Hop, hop, I’m a coding bunny,
Dancing through diffs, quick and funny.
Redirect renamed with a joyful cheer,
New providers and adapters now are here!
PostHog and JWTs make my code feel light—
Leaping in the repo, day and night!
🐇✨ Happy Hopping in our vast code delight!

✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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: 7

🔭 Outside diff range comments (1)
apps/web/src/auth/adapters/prisma.ts (1)

22-39: 💡 Verification agent

🧩 Analysis chain

Verify type safety of account fields.

The account creation maintains required fields but lacks explicit type checking. Consider adding runtime validation or type guards to ensure all required fields are present.


🏁 Script executed:

#!/bin/bash
# Search for Account schema definition and usage
ast-grep --pattern 'model Account {
  $$$
}'

Length of output: 46


I will generate additional commands to search for the Prisma schema definition and locate any references to a "model Account" in the codebase. This will help us confirm if there is runtime type validation for the expected account fields as part of the Prisma schema or elsewhere.


🏁 Script executed:

#!/bin/bash
echo "Listing all Prisma schema files:"
fd "\.prisma$"
echo "Searching for 'model Account' definition:"
rg 'model\s+Account'

Length of output: 307


Account Type Safety Concerns in linkAccount Adapter

  • The Prisma schema for Account (located at packages/database/prisma/schema.prisma) defines the Account model, but the adapter code relies on type assertions (e.g., as string, as number) without performing runtime validation.
  • This reliance on compile-time type-checking and DB schema constraints means the adapter does not verify at runtime that the provided account fields are properly set and formatted.
  • To ensure robustness, consider implementing explicit runtime checks or using a validation library (such as zod) before calling prisma.account.create.
🧹 Nitpick comments (21)
apps/web/src/middleware.ts (2)

16-46: Consider restricting non-logged-in users.
Currently, the middleware only redirects logged-in users away from /login. If your application requires authenticated access to certain pages, add logic to handle the case where !isLoggedIn && restrictedRoute. Otherwise, unauthenticated users can still access potentially restricted URLs.

 if (!isLoggedIn && restrictedPaths.test(newUrl.pathname)) {
+  newUrl.pathname = "/login";
+  return NextResponse.redirect(newUrl);
 }

41-46: Error handling for PostHog interactions.
If withPostHog fails (e.g., network error), the request may be delayed or fail. Consider wrapping the analytics call in a try/catch or providing a fallback to avoid impacting user requests when analytics is unavailable.

apps/web/src/next-auth.ts (2)

51-68: Event-based analytics capturing.
The signIn event is well-instrumented with PostHog. If this event is critical, ensure errors in PostHog do not disrupt user sign-in. A non-blocking approach often improves reliability.


128-167: JWT callback structure is robust.
Nicely updates the token with user-specific info, verifying that the user is not a guest. Good use of Zod for partial session updates. Logging the parse error to console.error might suffice in dev environments, but consider a more centralized logging approach for production.

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

1-14: LGTM! Consider adding type annotations.

The guest provider implementation is clean and follows next-auth v5 patterns. The unique ID generation ensures no collisions between guest users.

Consider adding type annotations for better maintainability:

-  async authorize() {
+  async authorize(): Promise<{ id: string; email: null }> {
apps/web/src/auth/providers/google.ts (1)

3-11: Add error handling for missing configuration.

Consider adding debug logging when configuration is missing to help with troubleshooting deployment issues.

 export function GoogleProvider() {
   if (process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET) {
     return BaseGoogleProvider({
       clientId: process.env.GOOGLE_CLIENT_ID,
       clientSecret: process.env.GOOGLE_CLIENT_SECRET,
       allowDangerousEmailAccountLinking: true,
     });
+  } else {
+    console.debug('Google provider not configured - skipping initialization');
   }
 }
apps/web/src/auth/get-optional-providers.ts (1)

7-11: LGTM! Consider improving type safety.

The implementation cleanly handles optional providers and correctly filters out undefined values.

Consider making the return type more explicit:

-export function getOptionalProviders() {
+export function getOptionalProviders(): Provider[] {
   return [OIDCProvider(), GoogleProvider(), MicrosoftProvider()].filter(
     Boolean,
-  ) as Provider[];
+  );
apps/web/src/auth/providers/microsoft.ts (2)

13-14: Consider using tenant-specific well-known URL.

The well-known URL uses 'common' instead of the specific tenant ID. While this works, using a tenant-specific URL is recommended for better security.

-      wellKnown:
-        "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration",
+      wellKnown: `https://login.microsoftonline.com/${process.env.MICROSOFT_TENANT_ID}/v2.0/.well-known/openid-configuration`,

3-17: Add error handling for missing configuration.

Consider adding debug logging when configuration is missing to help with troubleshooting deployment issues.

 export function MicrosoftProvider() {
   if (
     process.env.MICROSOFT_TENANT_ID &&
     process.env.MICROSOFT_CLIENT_ID &&
     process.env.MICROSOFT_CLIENT_SECRET
   ) {
     return MicrosoftEntraID({
       name: "Microsoft",
       clientId: process.env.MICROSOFT_CLIENT_ID,
       clientSecret: process.env.MICROSOFT_CLIENT_SECRET,
       wellKnown:
         "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration",
     });
+  } else {
+    console.debug('Microsoft provider not configured - skipping initialization');
   }
 }
apps/web/src/auth/is-email-blocked.ts (1)

1-19: LGTM! Email blocking implementation looks good.

The implementation correctly handles pattern matching and follows the non-async requirement.

Consider these improvements:

  1. Pre-compile regex patterns for better performance:
 export const isEmailBlocked = (email: string) => {
   if (process.env.ALLOWED_EMAILS) {
     const allowedEmails = process.env.ALLOWED_EMAILS.split(",");
+    // Pre-compile patterns at module level for better performance
+    const patterns = allowedEmails.map((allowedEmail) =>
+      new RegExp(
+        `^${allowedEmail
+          .replace(/[.+?^${}()|[\]\\]/g, "\\$&")
+          .replaceAll(/[*]/g, ".*")}$`
+      )
+    );
     // Check whether the email matches enough of the patterns specified in ALLOWED_EMAILS
-    const isAllowed = allowedEmails.some((allowedEmail) => {
-      const regex = new RegExp(
-        `^${allowedEmail
-          .replace(/[.+?^${}()|[\]\\]/g, "\\$&")
-          .replaceAll(/[*]/g, ".*")}$`,
-      );
-      return regex.test(email);
-    });
+    const isAllowed = patterns.some((pattern) => pattern.test(email));
  1. Add email format validation:
 export const isEmailBlocked = (email: string) => {
+  // Basic email format validation
+  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+  if (!emailRegex.test(email)) {
+    return true;
+  }
   if (process.env.ALLOWED_EMAILS) {
packages/posthog/src/server/index.ts (1)

18-28: LGTM! Higher-order function implementation looks good.

The withPosthog function correctly wraps the handler and ensures PostHog events are flushed after the response is generated.

Consider enhancing the error handling to include more context:

-      console.error("Failed to flush PostHog events:", error);
+      console.error("Failed to flush PostHog events:", {
+        error,
+        url: req.url,
+        method: req.method,
+      });
apps/web/src/auth/migrations/next-auth-v4/jwt.ts (1)

7-16: Add error handling and consider configuring clock tolerance.

The JWT decoding implementation could be improved in two ways:

  1. Add error handling for decryption failures
  2. Consider making the clock tolerance configurable

Consider this implementation:

 export async function decode(params: JWTDecodeParams): Promise<JWT | null> {
   /** @note empty `salt` means a session token. See {@link JWTDecodeParams.salt}. */
   const { token, secret, salt = "" } = params;
   if (!token) return null;
   const encryptionSecret = await getDerivedEncryptionKey(secret, salt);
-  const { payload } = await jwtDecrypt(token, encryptionSecret, {
-    clockTolerance: 15,
-  });
-  return payload;
+  try {
+    const { payload } = await jwtDecrypt(token, encryptionSecret, {
+      clockTolerance: process.env.JWT_CLOCK_TOLERANCE_SECONDS 
+        ? parseInt(process.env.JWT_CLOCK_TOLERANCE_SECONDS) 
+        : 15,
+    });
+    return payload;
+  } catch (error) {
+    console.error('Failed to decrypt JWT:', error);
+    return null;
+  }
 }
apps/web/src/auth/providers/email.ts (2)

8-11: Add environment variable validation.

Consider validating the NOREPLY_EMAIL environment variable to ensure it's properly set.

Add this validation at the top of the file:

if (!process.env.NOREPLY_EMAIL) {
  throw new Error('NOREPLY_EMAIL environment variable is not set');
}

15-41: Enhance error handling and code organization.

Consider these improvements:

  1. Add error handling for email sending failures
  2. Extract magic link construction to a separate function

Consider this refactored implementation:

+const constructMagicLink = (url: string) => 
+  absoluteUrl("/auth/login", { magicLink: url });

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

   if (user) {
+    try {
       await getEmailClient(user.locale ?? undefined).sendTemplate(
         "LoginEmail",
         {
           to: email,
           props: {
-            magicLink: absoluteUrl("/auth/login", {
-              magicLink: url,
-            }),
+            magicLink: constructMagicLink(url),
             code: token,
           },
         },
       );
+    } catch (error) {
+      console.error('Failed to send verification email:', error);
+      throw new Error('Failed to send verification email');
+    }
   }
 },
apps/web/src/auth/providers/oidc.ts (1)

8-12: Consider using env validation for OIDC configuration.

The environment variables check could be moved to a validation layer to fail fast during startup rather than runtime.

-  if (
-    process.env.OIDC_DISCOVERY_URL &&
-    process.env.OIDC_CLIENT_ID &&
-    process.env.OIDC_CLIENT_SECRET
-  ) {
+  const config = {
+    discoveryUrl: process.env.OIDC_DISCOVERY_URL,
+    clientId: process.env.OIDC_CLIENT_ID,
+    clientSecret: process.env.OIDC_CLIENT_SECRET,
+  };
+
+  if (config.discoveryUrl && config.clientId && config.clientSecret) {
apps/web/src/auth/providers/registration-token.ts (1)

39-41: Add logging for failed registration attempts.

Consider adding logging for security monitoring when user lookup fails.

 if (user) {
   return user;
+} else {
+  console.warn(`Failed registration attempt for email: ${payload.email}`);
 }
apps/web/src/auth/adapters/prisma.ts (1)

16-20: Consider using satisfies instead of type assertion.

The change from satisfies Adapter to as Adapter reduces type safety. Type assertions can hide potential type mismatches, while satisfies ensures compile-time verification.

-  } as Adapter;
+  } satisfies Adapter;
apps/web/src/auth/migrations/next-auth-v4/types.ts (1)

8-13: Consider adding more specific JWT fields.

While the JWT interface extends DefaultJWT correctly, consider adding application-specific fields that were observed in the session callback (locale, timeFormat, timeZone, weekStart).

-export interface JWT extends Record<string, unknown>, DefaultJWT {}
+export interface JWT extends Record<string, unknown>, DefaultJWT {
+  locale?: string;
+  timeFormat?: string;
+  timeZone?: string;
+  weekStart?: number;
+}
apps/web/src/next-auth.config.ts (1)

7-11: Consider using environment variables for public routes.

The public routes configuration could be more flexible by using environment variables, especially for feature-flag dependent routes.

-const publicRoutes = ["/login", "/register", "/invite/", "/poll/", "/auth"];
+const publicRoutes = process.env.PUBLIC_ROUTES?.split(",") ?? [
+  "/login",
+  "/register",
+  "/invite/",
+  "/poll/",
+  "/auth",
+];

 if (isQuickCreateEnabled) {
   publicRoutes.push("/quick-create", "/new");
 }
apps/web/src/app/[locale]/(auth)/login/components/sso-provider.tsx (1)

40-48: Consider adding prop type validation.

The component could benefit from runtime prop type validation.

+import { z } from "zod";
+
+const ssoProviderSchema = z.object({
+  providerId: z.string(),
+  name: z.string(),
+  redirectTo: z.string().optional(),
+});
+
 export function SSOProvider({
   providerId,
   name,
   redirectTo,
-}: {
-  providerId: string;
-  name: string;
-  redirectTo?: string;
-}) {
+}: z.infer<typeof ssoProviderSchema>) {
+  ssoProviderSchema.parse({ providerId, name, redirectTo });
apps/web/src/app/[locale]/(auth)/login/components/login-email-form.tsx (1)

56-64: LGTM! Successful parameter migration with a minor suggestion.

The changes from callbackUrl to redirectTo are correctly implemented in both the signIn call and the router navigation.

Consider extracting the redirection URL to a constant to avoid repetition:

+const redirectUrl = searchParams?.get("redirectTo") ?? "";
 await signIn("email", {
   email: identifier,
-  redirectTo: searchParams?.get("redirectTo") ?? undefined,
+  redirectTo: redirectUrl || undefined,
   redirect: false,
 });
 router.push(
-  `/login/verify?redirectTo=${encodeURIComponent(
-    searchParams?.get("redirectTo") ?? "",
-  )}`,
+  `/login/verify?redirectTo=${encodeURIComponent(redirectUrl)}`,
 );
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8ff8058 and 678cd2d.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (45)
  • apps/web/i18next-scanner.config.js (1 hunks)
  • apps/web/package.json (2 hunks)
  • apps/web/public/locales/en/app.json (1 hunks)
  • apps/web/src/app/[locale]/(admin)/settings/profile/delete-account-dialog.tsx (1 hunks)
  • apps/web/src/app/[locale]/(auth)/auth/login/components/login-page.tsx (3 hunks)
  • apps/web/src/app/[locale]/(auth)/login/actions.ts (1 hunks)
  • apps/web/src/app/[locale]/(auth)/login/components/login-email-form.tsx (1 hunks)
  • apps/web/src/app/[locale]/(auth)/login/components/login-with-oidc.tsx (1 hunks)
  • apps/web/src/app/[locale]/(auth)/login/components/sso-provider.tsx (3 hunks)
  • apps/web/src/app/[locale]/(auth)/login/page.tsx (3 hunks)
  • apps/web/src/app/[locale]/(auth)/login/verify/components/otp-form.tsx (1 hunks)
  • apps/web/src/app/[locale]/(auth)/register/verify/components/otp-form.tsx (1 hunks)
  • apps/web/src/app/[locale]/layout.tsx (2 hunks)
  • apps/web/src/app/api/auth/[...nextauth]/route.ts (1 hunks)
  • apps/web/src/app/api/notifications/unsubscribe/route.ts (2 hunks)
  • apps/web/src/app/api/stripe/checkout/route.ts (2 hunks)
  • apps/web/src/app/api/stripe/portal/route.ts (2 hunks)
  • apps/web/src/app/api/trpc/[trpc]/route.ts (2 hunks)
  • apps/web/src/app/api/user/verify-email-change/route.ts (2 hunks)
  • apps/web/src/auth.ts (0 hunks)
  • apps/web/src/auth/adapters/prisma.ts (2 hunks)
  • apps/web/src/auth/get-optional-providers.ts (1 hunks)
  • apps/web/src/auth/is-email-blocked.ts (1 hunks)
  • apps/web/src/auth/migrations/next-auth-cookie-migration.ts (1 hunks)
  • apps/web/src/auth/migrations/next-auth-v4/jwt.ts (1 hunks)
  • apps/web/src/auth/migrations/next-auth-v4/types.ts (1 hunks)
  • apps/web/src/auth/providers/email.ts (1 hunks)
  • apps/web/src/auth/providers/google.ts (1 hunks)
  • apps/web/src/auth/providers/guest.ts (1 hunks)
  • apps/web/src/auth/providers/microsoft.ts (1 hunks)
  • apps/web/src/auth/providers/oidc.ts (1 hunks)
  • apps/web/src/auth/providers/registration-token.ts (1 hunks)
  • apps/web/src/components/login-link.tsx (1 hunks)
  • apps/web/src/components/poll/language-selector.tsx (1 hunks)
  • apps/web/src/components/register-link.tsx (1 hunks)
  • apps/web/src/features/quick-create/lib/get-guest-polls.ts (1 hunks)
  • apps/web/src/middleware.ts (1 hunks)
  • apps/web/src/next-auth.config.ts (1 hunks)
  • apps/web/src/next-auth.ts (1 hunks)
  • apps/web/src/pages/api/auth/[...nextauth].ts (0 hunks)
  • apps/web/src/trpc/routers/auth.ts (1 hunks)
  • apps/web/src/trpc/server/create-ssr-helper.ts (1 hunks)
  • apps/web/tests/create-delete-poll.spec.ts (1 hunks)
  • package.json (1 hunks)
  • packages/posthog/src/server/index.ts (2 hunks)
💤 Files with no reviewable changes (2)
  • apps/web/src/pages/api/auth/[...nextauth].ts
  • apps/web/src/auth.ts
✅ Files skipped from review due to trivial changes (2)
  • package.json
  • apps/web/src/trpc/routers/auth.ts
🧰 Additional context used
🧠 Learnings (1)
apps/web/src/auth/is-email-blocked.ts (1)
Learnt from: lukevella
PR: lukevella/rallly#1189
File: apps/web/src/utils/auth.ts:202-202
Timestamp: 2024-11-12T09:08:14.221Z
Learning: The `isEmailBlocked()` function in `apps/web/src/utils/auth.ts` is not asynchronous.
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: Unit tests
  • GitHub Check: Integration tests
  • GitHub Check: Linting
  • GitHub Check: Type check
🔇 Additional comments (44)
apps/web/src/middleware.ts (5)

3-5: Imports look good.
No issues found with these import lines; they conform to Next.js and the NextAuth v5 upgrade path.


8-9: Migration import usage is clear.
The withAuthMigration import correctly integrates the cookie migration flow. Ensure that the imported file handles all edge cases (e.g., missing cookies) so users aren’t logged out unexpectedly after the migration.


11-11: Transition to NextAuth v5 approach.
Defining { auth } from the NextAuth(nextAuthConfig) call neatly scopes session handling and helps with the new v5 modular pattern. No issues noticed here.


29-36: Locale derivation might cause 404s for invalid or empty locales.
If getLocaleFromHeader(req) returns a locale not in supportedLocales, the user might be redirected to an unknown path. Consider validating or defaulting to a fallback locale.


49-57: Clear, composable middleware flow.
The pattern of first running withAuthMigration, then falling back to the standard withAuth is a neat approach to keep cross-cutting concerns separated.

apps/web/src/next-auth.ts (4)

1-2: Prisma and PostHog imports integration.
Combining database calls and analytics in the same authentication flow can be powerful but watch for potential performance bottlenecks if large user lookups happen frequently.


25-34: Adapter’s migrateData usage.
Invoking auth() during migrateData might create an unexpected nested session call if not carefully handled. Confirm that no infinite loops or concurrency issues arise when merging guest users into real accounts.


35-42: Dynamic providers approach.
Including optional providers with a simple .filter(Boolean) is straightforward. Confirm that uninitialized providers don’t cause runtime errors if environment variables are missing.


70-127: Comprehensive signIn callback checks.
You’ve handled email blocking, unverified emails, and social login merges. This thorough approach seems solid.

apps/web/src/app/api/auth/[...nextauth]/route.ts (1)

1-6: Wrapping NextAuth handlers with PostHog.
This integration is simple and concise, which is ideal. If you need to handle additional HTTP methods (e.g., PATCH or PUT), be sure to wrap them as well.

apps/web/src/components/login-link.tsx (1)

15-15: LGTM! Parameter name updated for next-auth v5.

The change from callbackUrl to redirectTo aligns with the breaking changes introduced in next-auth v5.

apps/web/src/app/[locale]/(auth)/login/actions.ts (1)

7-27: LGTM! Improved database query efficiency and security.

The changes improve the implementation by:

  1. Using findUnique instead of count for better efficiency
  2. Setting the cookie only when a user is found
  3. Properly configuring cookie security flags
apps/web/i18next-scanner.config.js (1)

4-4: LGTM! Updated exclusion pattern for next-auth files.

The pattern correctly excludes all next-auth related files, aligning with the restructured authentication implementation.

apps/web/src/app/[locale]/(auth)/login/components/login-with-oidc.tsx (1)

9-9: LGTM! Parameter renaming aligns with next-auth v5.

The changes correctly update the parameter name from callbackUrl to redirectTo in both the function signature and usage, maintaining consistency with the next-auth v5 upgrade.

Also applies to: 12-12, 18-18

apps/web/src/features/quick-create/lib/get-guest-polls.ts (1)

3-3: LGTM! Session retrieval updated to use next-auth v5.

The changes correctly update the session retrieval method from getServerSession to auth(), maintaining consistency with the next-auth v5 upgrade.

Also applies to: 6-6

apps/web/src/components/register-link.tsx (1)

20-20: LGTM! URL parameter updated to align with next-auth v5.

The changes correctly update the URL parameter from callbackUrl to redirectTo in the router.push call, maintaining consistency with the next-auth v5 upgrade.

apps/web/src/auth/migrations/next-auth-v4/jwt.ts (1)

18-29: LGTM! Secure key derivation implementation.

The implementation follows cryptographic best practices:

  • Uses HKDF with SHA-256
  • Generates appropriate key length (32 bytes)
  • Includes context separation through the info parameter
apps/web/src/components/poll/language-selector.tsx (1)

21-21: Verify UI consistency after removing button variant.

The removal of variant="ghost" might affect the visual consistency with other buttons in the application.

Please confirm if this change aligns with the UI design system and doesn't create visual inconsistencies.

apps/web/tests/create-delete-poll.spec.ts (1)

34-34: LGTM! URL parameter updated for next-auth v5.

The change from callbackUrl to redirectTo aligns with the next-auth v5 upgrade.

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

12-14: LGTM! Secure token generation.

Using generateOtp for verification tokens is a secure approach.

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

23-23: Verify the security implications of allowDangerousEmailAccountLinking.

Setting allowDangerousEmailAccountLinking to true could potentially lead to account hijacking if the OIDC provider's email verification process is not robust.

✅ Verification successful

🌐 Web query:

What are the security implications of allowDangerousEmailAccountLinking in next-auth?

💡 Result:

Enabling allowDangerousEmailAccountLinking in NextAuth.js introduces significant security risks if not used cautiously. Here's a breakdown of the implications:

Security Risks

  1. Account Hijacking:
    Attackers can create OAuth accounts using another user’s email address (without verification) and gain access to their existing account on your platform[1][3][5]. This bypasses standard safeguards since many OAuth providers don’t enforce email verification by default[1].

  2. Trust Dependency:
    You must fully trust all enabled OAuth providers to securely verify email ownership. For example:

    • ✅ Safe for internal/controlled OAuth providers (e.g., corporate SSO).
    • ❌ Risky for arbitrary third-party providers (e.g., untrusted OAuth services)[1][6].
  3. No Fallback Verification:
    Unlike email/password or magic link flows (which require explicit email verification), OAuth-based linking skips secondary confirmation steps[2][6].

When Is It Acceptable?

  • Trusted Providers Only: Use with major providers like Google or GitHub if you’re confident they enforce email verification[7][8].
  • Controlled Environments: Internal tools where all OAuth providers are vetted and managed internally[1][6].

Mitigation Strategies

  • Avoid enabling this flag for public-facing apps with multiple third-party OAuth providers[1][5].
  • Implement manual account linking flows (planned for future NextAuth.js releases)[1].
  • For magic links + OAuth combinations, ensure your database stores verified emails and use allowDangerousEmailAccountLinking only after auditing provider security practices[2][6].

Default Behavior

Without this flag, NextAuth.js blocks automatic linking unless:

  • The user is already signed in (manual session-based linking)[3].
  • Using email-first authentication followed by OAuth (email verification acts as a trust anchor)[1][2].

In summary, allowDangerousEmailAccountLinking shifts security responsibility to OAuth providers. Use it sparingly and only when you explicitly trust every provider involved[1][5][9].

Citations:


Security Implications of allowDangerousEmailAccountLinking

The external resources confirm that enabling allowDangerousEmailAccountLinking in NextAuth.js carries serious risks:

  • Account Hijacking: Attackers might hijack accounts by linking an OAuth account to an existing email if the provider’s email verification isn’t thorough.
  • Trust Dependency: You must fully trust all OAuth providers being used—the feature is only safe with providers that enforce strict email verification.
  • Lack of Fallback Verification: Unlike traditional email/password or magic link flows, this option bypasses a secondary verification step.

Recommendation:
Use this flag only in environments where all OAuth/OIDC providers are controlled and reliably verify emails. For public-facing applications or loosely managed providers, it’s safer to disable the flag or implement additional manual linking safeguards.

apps/web/src/trpc/server/create-ssr-helper.ts (1)

11-13: Verify session type compatibility with next-auth v5.

The migration from getServerSession to auth() looks good, but ensure that the session type remains compatible with the rest of the application.

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Search for session type usage across the codebase
rg -t typescript "Session" --glob "!node_modules"

Length of output: 84


I'm going to gather additional details to verify the session type usage across the codebase. In particular, I'll search for any definitions of a Session type and review how auth() is used, as well as confirm the NextAuth version in our project.

Please run the following script:

This will help us verify:

  • Whether any Session type is defined or referenced in the codebase.
  • How auth() is used in context.
  • The version of NextAuth installed (to ensure we're on v5).

Once we have these results, we can conclude the session type compatibility.


🏁 Script executed:

[run_scripts]
#!/bin/bash
echo "Searching for Session type definitions (interface and type):"
rg "interface Session" -g "*.ts" --glob "!node_modules"
rg "type Session" -g "*.ts" --glob "!node_modules"

echo ""
echo "Searching for auth() usage in TypeScript files:"
rg "auth\\(" -g "*.ts" --glob "!node_modules"

echo ""
echo "Checking next-auth version in package.json:"
rg '"next-auth":' package.json

Length of output: 1503


Session type migration verified as compatible.
Our investigation shows that the session type is defined in the file apps/web/declarations/next-auth.d.ts and is consistently used across multiple files invoking auth(). Although the version check in package.json did not return an explicit next-auth version, the uniform usage of auth() in key areas (including apps/web/src/trpc/server/create-ssr-helper.ts) indicates that the migration to next-auth v5 has been applied correctly without breaking session type compatibility.

apps/web/src/app/[locale]/layout.tsx (1)

33-33: Verify SessionProvider compatibility with next-auth v5.

The migration from getServerSession to auth() looks good, but ensure that the SessionProvider component is compatible with the new session format.

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Search for SessionProvider usage and imports
rg -t typescript "SessionProvider"

Length of output: 69


Below is a revised search script that uses a glob pattern rather than the unrecognized file type flag. This will search for references to SessionProvider across common JavaScript/TypeScript files. Please run this script and share its output so that we can fully verify whether the SessionProvider usage is present and compatible with the new session format.

This will help us see if the component is in use and inspect its implementation for compatibility with next-auth v5.


🏁 Script executed:

[run_scripts]
#!/bin/bash
# Search for SessionProvider usage and imports across JavaScript and TypeScript files.
rg --glob '*.{ts,tsx,js,jsx}' "SessionProvider"

Length of output: 960


SessionProvider Compatibility Verified

The custom SessionProvider component in apps/web/src/auth/session-provider.tsx is a thin wrapper that directly re-exports NextAuth’s SessionProvider from next-auth/react. The session returned by auth() in apps/web/src/app/[locale]/layout.tsx is passed through correctly. Based on the current repository usage, there’s no indication that the new session format from next-auth v5 poses any compatibility issues with our SessionProvider.

apps/web/src/app/api/trpc/[trpc]/route.ts (1)

7-7: LGTM! Successfully migrated to next-auth v5.

The migration from getServerSession to auth() is correctly implemented, maintaining compatibility with the existing session structure and error handling.

Also applies to: 18-18

apps/web/src/app/api/user/verify-email-change/route.ts (1)

6-6: LGTM! Successfully migrated to next-auth v5 with updated redirect parameter.

The changes correctly implement:

  1. Migration from getServerSession to auth()
  2. Updated redirect parameter from callbackUrl to redirectTo

Also applies to: 53-53, 57-57

apps/web/src/app/api/notifications/unsubscribe/route.ts (1)

6-6: LGTM! Successfully migrated to next-auth v5.

The migration from getServerSession to auth() is correctly implemented while maintaining all security checks and cookie handling.

Also applies to: 17-17

apps/web/src/auth/migrations/next-auth-v4/types.ts (2)

1-6: LGTM! Well-structured DefaultJWT interface.

The interface correctly extends Record<string, unknown> to allow for additional properties while maintaining type safety for core JWT fields.


15-44: LGTM! Well-documented encode/decode params.

The JWTEncodeParams and JWTDecodeParams interfaces are thoroughly documented with clear parameter descriptions and default values.

apps/web/src/next-auth.config.ts (1)

33-48: LGTM! Robust authorization logic.

The authorized callback correctly handles:

  • Authentication state checking
  • Public route verification
  • Redirect URL construction with preserved path
apps/web/src/auth/migrations/next-auth-cookie-migration.ts (1)

51-59: LGTM! Clean middleware implementation.

The middleware correctly handles the migration response and follows the pattern of returning null when no action is needed.

apps/web/src/app/[locale]/(auth)/login/components/sso-provider.tsx (2)

18-27: LGTM! Updated Microsoft provider name.

The provider name has been correctly updated from "azure-ad" to "microsoft-entra-id" to align with Microsoft's rebranding.


60-62: LGTM! Updated signIn parameter name.

The signIn function correctly uses the new redirectTo parameter name instead of callbackUrl.

apps/web/src/app/api/stripe/portal/route.ts (1)

8-8: LGTM! Correctly upgraded to next-auth v5's session handling.

The changes correctly implement next-auth v5's new session handling approach by replacing getServerSession with auth().

Let's verify that all instances of getServerSession have been replaced with auth():

Also applies to: 35-35

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Search for any remaining instances of getServerSession
rg "getServerSession" -A 3

Length of output: 26


All Instances of getServerSession Have Been Successfully Replaced

  • The search across the codebase with rg "getServerSession" -A 3 produced no output, indicating that no occurrences of getServerSession remain.
  • This confirms that the upgrade to next‑auth v5’s session handling (using auth()) has been fully applied.
apps/web/src/app/[locale]/(auth)/login/verify/components/otp-form.tsx (1)

53-53: LGTM! Correctly updated to next-auth v5's redirection parameter.

The change from callbackUrl to redirectTo aligns with next-auth v5's standardized redirection parameters.

Let's verify that all instances of callbackUrl have been replaced with redirectTo:

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Search for any remaining instances of callbackUrl
rg "callbackUrl" -A 3

Length of output: 21


Verification Complete: All instances of callbackUrl have been replaced.

The search using rg "callbackUrl" produced no output, confirming that there are no remaining occurrences in the codebase and that the update to use redirectTo per next-auth v5 is in effect.

  • File checked: apps/web/src/app/[locale]/(auth)/login/verify/components/otp-form.tsx
  • Change verified: Replaced all occurrences of callbackUrl with redirectTo
apps/web/src/app/[locale]/(auth)/login/page.tsx (2)

4-6: LGTM! Improved provider initialization with next-auth v5.

The changes correctly implement next-auth v5's provider-specific approach:

  • Explicit provider imports improve type safety
  • Individual provider initialization allows better tree-shaking

Also applies to: 32-34


27-27: LGTM! Consistently updated redirection parameters.

The changes correctly update all instances of callbackUrl to redirectTo in accordance with next-auth v5's API changes.

Also applies to: 57-57, 67-68

apps/web/src/app/[locale]/(auth)/auth/login/components/login-page.tsx (1)

20-21: LGTM! Enhanced error handling for magic links.

The changes improve user experience by:

  • Adding proper error state management
  • Providing clear feedback for invalid/expired links
  • Supporting internationalized error messages

Also applies to: 29-47, 86-86

apps/web/src/app/api/stripe/checkout/route.ts (1)

8-8: LGTM! Successful migration to next-auth v5.

The change from getServerSession() to auth() is correctly implemented while maintaining the existing authentication flow.

Also applies to: 17-17

✅ Verification successful

🌐 Web query:

What are the key differences between getServerSession and auth() in next-auth v5?

💡 Result:

In NextAuth v5, getServerSession and auth() serve similar purposes but with key differences in implementation and scope:

Core Differences

1. Configuration Handling

  • getServerSession: Requires explicit passing of authOptions (configuration object) every time[3][7]
  • auth(): Automatically inherits configuration from centralized auth.ts file[1][6][10]

2. Usage Context

Feature getServerSession auth()
Server Components Required manual config passing[3] Direct usage without config[1][6]
API Routes Needed request/response objects[7] Accepts context object directly[1]
Middleware Complex setup Simplified integration[6][10]

3. Return Behavior

  • getServerSession: Returns null for unauthenticated users[3][7]
  • auth(): Returns session object with user property containing filtered data (name/email/image)[6][10]

Code Comparison

Legacy Approach (v4):

import { getServerSession } from "next-auth/next"
import { authOptions } from "pages/api/auth/[...nextauth]"

export async function Page() {
  const session = await getServerSession(authOptions)
}

v5 Approach:

import { auth } from "@/auth" // Central config import

export async function Page() {
  const session = await auth()
}

Key Advantages of auth()

  • Simplified Imports: Eliminates need for separate authOptions imports[1][6]
  • Unified Interface: Works identically in pages, app router, and API routes[1][10]
  • Security Filtering: Automatically strips sensitive user data from sessions[6][10]
  • Middleware Integration: Enables cleaner authentication flows in edge runtime[6][10]

The migration to auth() reflects NextAuth v5's shift toward App Router optimization and reduced boilerplate[1][6]. Existing getServerSession usage should be updated to leverage the simplified auth() pattern for better maintainability[1][10].

Citations:


LGTM! The migration from getServerSession() to auth() is correctly implemented.

The use of auth() aligns with NextAuth v5 best practices by inheriting the centralized configuration and streamlining the authentication process without manually passing configuration objects. The web query confirms that auth() simplifies usage and returns a correctly filtered session object, making the change appropriate for both API routes and middlewares.

  • Verify that the import in apps/web/src/app/api/stripe/checkout/route.ts (lines 8 and 17) is consistent with the centralized configuration.
apps/web/src/app/[locale]/(auth)/register/verify/components/otp-form.tsx (1)

71-71: LGTM! Successful parameter migration.

The change from callbackUrl to redirectTo is correctly implemented, maintaining the expected redirection behavior after OTP verification.

apps/web/src/app/[locale]/(admin)/settings/profile/delete-account-dialog.tsx (1)

41-42: LGTM! Successful parameter migration.

The change from callbackUrl to redirectTo in the signOut call is correctly implemented, maintaining the expected post-deletion redirection behavior.

apps/web/package.json (3)

21-21: Verify prisma adapter compatibility with next-auth v5.

Let's verify if the prisma adapter version is compatible with next-auth v5 beta.

Also applies to: 71-71

✅ Verification successful

🌐 Web query:

What version of @auth/prisma-adapter is required for next-auth v5?

💡 Result:

NextAuth.js v5 requires using the @auth/prisma-adapter package instead of the legacy @next-auth/prisma-adapter. This change aligns with Auth.js's framework-agnostic rebranding[1][2][7].

Key details:

  • Installation:
    npm install @auth/prisma-adapter
  • Compatibility:
    The latest stable version as of February 2025 is v2.7.4, which supports Next.js 14+ and Prisma Client v5.12.0+[5][7].
  • Configuration:
    import { PrismaAdapter } from "@auth/prisma-adapter"
    import { PrismaClient } from "@prisma/client"
    
    const prisma = new PrismaClient()
    
    export const { auth } = NextAuth({
      adapter: PrismaAdapter(prisma)
    })

This replaces older @next-auth/prisma-adapter installations[1][8]. Ensure your Prisma schema matches Auth.js's requirements[3][5][9].

Citations:


Compatibility Verified with NextAuth v5
The installed version of @auth/prisma-adapter (^2.7.4) in apps/web/package.json is confirmed to be compatible with NextAuth v5 as it matches the required version (v2.7.4) for proper support.

  • Verify in file: apps/web/package.json (lines 21 and 71)

26-26: Verify if new dependencies are required.

The new dependencies @panva/hkdf and jose appear to be required for next-auth v5's cryptographic operations. Let's verify this.

Also applies to: 63-63

✅ Verification successful

🌐 Web query:

Are @panva/hkdf and jose required dependencies for next-auth v5?

💡 Result:

@panva/hkdf and jose are required internal dependencies for NextAuth.js v5 but aren't explicitly listed as peer dependencies in most setups. Here's the breakdown:

  1. @panva/hkdf

    • Used for key derivation in authentication flows (e.g., token encryption/decryption)[4][6].
    • Automatically included with NextAuth.js v5 installations via npm/yarn/bun[6][8].
    • May require manual intervention in non-standard environments (e.g., Bun users must add transpile: ['@panva/hkdf'] to build configs)[2].
  2. jose

    • Handles JWT/JWE operations[4][6].
    • Bundled with NextAuth.js v5[6][9].
    • Explicitly used in token decryption examples[4], confirming its critical role.

Key observations from search results:

  • NextAuth.js v5's package.json includes both as dependencies[6][8].
  • Migration docs confirm HKDF usage for secret derivation[7].
  • Build errors in Bun/Nuxt environments highlight their implicit requirement[2][9].

For standard Next.js projects using npm/yarn/pnpm, no manual installation is needed. Edge cases requiring explicit handling:

// Example Nuxt.js fix
export default defineNuxtConfig({
  build: {
    transpile: ['@auth/core', '@panva/hkdf']
  }
})

Citations:


Confirmation: Required Dependencies for NextAuth v5

The web query confirms that both @panva/hkdf and jose are indeed required for NextAuth v5's cryptographic operations. Key points are:

  • @panva/hkdf: Used for key derivation and is bundled internally with NextAuth.js v5. In standard environments (npm/yarn/pnpm), it's automatically installed.
  • jose: Handles JWT/JWE operations and is also bundled, making it critical to cryptographic functionality.

Thus, explicitly including these dependencies (as seen in apps/web/package.json at line 26 and line 63) is valid, especially for edge-case environments where manual configuration might be needed.


71-71: Verify next-auth v5 beta compatibility.

The upgrade to next-auth v5 beta is a major version change that could introduce breaking changes. Let's verify the compatibility and latest version.

❌ Verification inconclusive

🌐 Web query:

What are the breaking changes in next-auth v5 beta?

💡 Result:

NextAuth.js v5 introduces several breaking changes that developers should be aware of when migrating from previous versions:

Core Breaking Changes

  • OAuth 1.0 Deprecation: Full removal of OAuth 1.0 support (previously deprecated). Existing implementations using OAuth 1.0 providers will break[1][5].
  • Next.js 14 Requirement: Minimum required Next.js version is now 14.0[1][9].
  • Environment Variables: New AUTH_* prefix replaces NEXTAUTH_* for environment variables (e.g., AUTH_SECRET instead of NEXTAUTH_SECRET)[10].

API Changes

  • Imports Restructuring:
    • Replaced next-auth/next and next-auth/middleware imports with unified auth() function[1][4]
    • Deprecated methods: getServerSession, getSession, withAuth, getToken, and useSession[1]
  • Configuration Migration:
    • Auth config moved to top-level auth.ts file[2]
    • API routes simplified to export handlers directly:
      import { handlers } from '@/auth'
      export const { GET, POST } = handlers
      ```[1][2]

Provider Changes

  • Stricter OAuth/OIDC Compliance:
    • Existing OAuth providers might break due to spec-compliance improvements[1][5]
    • OIDC providers now require issuer field instead of wellKnown endpoint[5]
  • Type Changes:
    • OAuth providers must use type: "oidc" instead of "oauth"[5]

Session Management

  • New Session Handling:
    • Universal auth() replaces multiple session methods[1][4]
    • Session rotation now occurs automatically when passing response object[1]
  • Middleware Updates:
    // Previous
    export { default } from "next-auth/middleware"
    
    // New
    import { auth } from "@/auth"
    export default auth((req) => {...})
    ```[2]

Database Changes

  • Schema Cleanup:
    • Optional removal of deprecated fields:
      • oauth_token_secret
      • oauth_token
      • Third-party specific fields like GitHub's refresh_token_expires_in[1]
  • Adapter API: Simplified with core handling verification token hashing[8]

Development Considerations

  • Strict Mode Issues: Potential duplicate session updates in development due to React Strict Mode[6]
  • Edge Compatibility: New architecture supports Edge runtime but may require testing[1]

Developers should consult the official migration guide[1] and test authentication flows thoroughly when upgrading. The changes aim to simplify configuration while improving standards compliance and App Router support.

Citations:


NextAuth v5 Beta Compatibility – Manual Verification Advised

Based on the breaking changes documented for v5 beta, note that this version removes OAuth 1.0 support, requires Next.js 14, changes environment variable prefixes (from NEXTAUTH_* to AUTH_*), and refactors its API imports and middleware (now using a unified auth() function). Please ensure that:

  • Next.js Requirement: Your project is running on Next.js 14 or newer.
  • API & Middleware Changes: Any usage of legacy imports (e.g., next-auth/middleware or methods like getSession/withAuth) has been updated according to the new unified auth() approach.
  • Environment Variables: All environment variables conform to the new AUTH_* naming convention.
  • OAuth Providers: If you're using OAuth providers, confirm that they are compliant with the updated specifications (e.g., removal of OAuth 1.0 and adjustments for OIDC).

Since the package.json only reflects the version bump (without revealing code adjustments elsewhere), please double-check your configuration files and authentication logic for alignment with the v5 migration guide.

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

307-307: LGTM! Clear and actionable error message.

The new error message for magic link login is clear, follows the existing pattern, and provides a clear action for the user.

apps/web/src/auth/providers/google.ts Show resolved Hide resolved
apps/web/src/auth/providers/oidc.ts Show resolved Hide resolved
apps/web/src/next-auth.config.ts Show resolved Hide resolved
apps/web/src/auth/migrations/next-auth-cookie-migration.ts Outdated Show resolved Hide resolved
apps/web/src/auth/migrations/next-auth-cookie-migration.ts Outdated Show resolved Hide resolved
@vercel vercel bot temporarily deployed to Preview – landing February 12, 2025 03:46 Inactive
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: 1

🧹 Nitpick comments (1)
apps/web/src/middleware.ts (1)

28-36: Consider simplifying locale handling logic.

The current implementation has duplicate pathname modification logic. Consider extracting it to reduce code duplication.

 // Check if locale is specified in cookie
 let locale = req.auth?.user?.locale;
-if (locale && supportedLocales.includes(locale)) {
-  newUrl.pathname = `/${locale}${newUrl.pathname}`;
-} else {
+if (!locale || !supportedLocales.includes(locale)) {
   // Check if locale is specified in header
   locale = await getLocaleFromHeader(req);
-  newUrl.pathname = `/${locale}${newUrl.pathname}`;
 }
+newUrl.pathname = `/${locale}${newUrl.pathname}`;
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 678cd2d and 84135a2.

📒 Files selected for processing (10)
  • .env.development (0 hunks)
  • apps/web/.env.test (0 hunks)
  • apps/web/src/app/[locale]/(auth)/login/components/login-with-oidc.tsx (1 hunks)
  • apps/web/src/app/[locale]/(auth)/login/components/sso-provider.tsx (3 hunks)
  • apps/web/src/auth/legacy/helpers/jwt.ts (1 hunks)
  • apps/web/src/auth/legacy/helpers/types.ts (1 hunks)
  • apps/web/src/auth/legacy/next-auth-cookie-migration.ts (1 hunks)
  • apps/web/src/env.ts (2 hunks)
  • apps/web/src/middleware.ts (1 hunks)
  • scripts/docker-start.sh (0 hunks)
💤 Files with no reviewable changes (3)
  • scripts/docker-start.sh
  • apps/web/.env.test
  • .env.development
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/web/src/app/[locale]/(auth)/login/components/login-with-oidc.tsx
  • apps/web/src/app/[locale]/(auth)/login/components/sso-provider.tsx
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: Unit tests
  • GitHub Check: Integration tests
  • GitHub Check: Linting
  • GitHub Check: Type check
🔇 Additional comments (11)
apps/web/src/auth/legacy/helpers/types.ts (3)

1-13: LGTM! Well-structured JWT interfaces.

The JWT interfaces are well-defined with proper TypeScript practices and comprehensive documentation.


15-31: LGTM! Comprehensive encode parameters.

The JWTEncodeParams interface is well-documented with clear parameter descriptions and default values.


33-44: LGTM! Clear decode parameters.

The JWTDecodeParams interface mirrors the encode parameters structure with appropriate documentation.

apps/web/src/auth/legacy/helpers/jwt.ts (2)

7-17: Verify SECRET_PASSWORD environment variable.

The function uses process.env.SECRET_PASSWORD directly without validation. While the environment variable is validated in env.ts, it's good practice to use the validated version.

Consider importing from env.ts:

-    process.env.SECRET_PASSWORD,
+    env.SECRET_PASSWORD,

19-30: LGTM! Secure key derivation implementation.

The implementation uses HKDF (HMAC-based Key Derivation Function) with proper parameters for secure key derivation.

apps/web/src/auth/legacy/next-auth-cookie-migration.ts (2)

7-10: Use validated environment variables.

Similar to the JWT implementation, consider using the validated env.NEXT_PUBLIC_BASE_URL instead of accessing process.env directly.

-process.env.NEXT_PUBLIC_BASE_URL?.startsWith("https://") ?? false;
+env.NEXT_PUBLIC_BASE_URL.startsWith("https://");

19-46: LGTM! Comprehensive cookie migration implementation.

The implementation properly handles:

  • Cookie presence checks
  • Secure cookie attributes
  • Old cookie cleanup
apps/web/src/env.ts (1)

77-77: LGTM! Proper environment variable configuration.

The NEXT_PUBLIC_BASE_URL is:

  • Correctly prefixed for client usage
  • Validated as a URL
  • Added to both client and runtime configurations

Also applies to: 114-114

apps/web/src/middleware.ts (3)

1-13: LGTM! Clean initialization following next-auth v5 patterns.

The imports and initialization are well-structured, following the recommended patterns for next-auth v5.


15-46: LGTM! Well-structured auth middleware implementation.

The implementation correctly handles authentication, login redirection, locale management, and analytics integration following next-auth v5 patterns.


60-62: LGTM! Appropriate middleware route matching.

The matcher configuration correctly excludes static assets and API routes.

apps/web/src/middleware.ts Outdated Show resolved Hide resolved
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: 2

🧹 Nitpick comments (6)
apps/web/src/auth/middleware.ts (1)

9-15: Consider refining return type unions.

While the current implementation works well, you could improve type clarity by using undefined instead of void in union types, as suggested by the static analysis.

 export function withAuth(
   middleware: (
     req: NextAuthRequest,
-  ) => void | Response | Promise<void | Response>,
+  ) => undefined | Response | Promise<undefined | Response>,
- ): (req: NextRequest) => void | Response | Promise<void | Response> {
+ ): (req: NextRequest) => undefined | Response | Promise<undefined | Response> {
   return (req: NextRequest) => auth(middleware)(req, undefined as never);
 }
🧰 Tools
🪛 Biome (1.9.4)

[error] 12-12: void is confusing inside a union type.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)


[error] 13-13: void is confusing inside a union type.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)

apps/web/src/auth/legacy/next-auth-cookie-migration.ts (1)

19-21: Improve middleware function type signature.

The return type void | Response in the union is confusing. Consider using undefined | Response for better type clarity.

-  middleware: (req: NextRequest) => void | Response | Promise<void | Response>,
+  middleware: (req: NextRequest) => undefined | Response | Promise<undefined | Response>,
🧰 Tools
🪛 Biome (1.9.4)

[error] 20-20: void is confusing inside a union type.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)

apps/web/src/next-auth.ts (3)

43-47: Consider documenting error scenarios.

While the error page route is configured, it would be helpful to document the possible error scenarios that users might encounter.

Consider adding a comment listing the possible error scenarios and their corresponding error codes that will be handled by the error page.


52-68: Add error handling for analytics tracking.

While the PostHog tracking is well-implemented, it lacks error handling which could potentially affect the sign-in flow if PostHog is unavailable.

Consider wrapping the PostHog capture in a try-catch:

     signIn({ user, account }) {
       if (user.id) {
+        try {
           posthog?.capture({
             distinctId: user.id,
             event: "login",
             properties: {
               method: account?.provider,
               $set: {
                 name: user.name,
                 email: user.email,
                 timeZone: user.timeZone,
                 locale: user.locale,
               },
             },
           });
+        } catch (error) {
+          console.error('Failed to track login event:', error);
+        }
       }
     },

142-154: Consider caching user data lookup.

The user lookup in the jwt callback happens on every token refresh. Consider caching frequently accessed user data to improve performance.

Consider implementing a caching mechanism for user data:

import { LRUCache } from 'lru-cache'

const userCache = new LRUCache({
  max: 500, // Maximum number of items
  ttl: 1000 * 60 * 5, // Time to live: 5 minutes
})

// In jwt callback
const cachedUser = userCache.get(userId)
if (cachedUser) {
  // Use cached data
} else {
  const user = await prisma.user.findUnique(...)
  userCache.set(userId, user)
}
apps/web/src/auth/helpers/get-optional-providers.ts (1)

7-11: Consider adding error handling and caching.

The function logic is correct, but consider these improvements:

  1. Add error handling to gracefully handle provider initialization failures.
  2. Consider caching provider instances to avoid unnecessary re-initialization.

Here's a suggested implementation:

+const providerCache: Provider[] = [];
+
 export function getOptionalProviders() {
+  if (providerCache.length > 0) {
+    return providerCache;
+  }
+
+  const providers = [
+    tryInitProvider(() => OIDCProvider()),
+    tryInitProvider(() => GoogleProvider()),
+    tryInitProvider(() => MicrosoftProvider()),
+  ].filter(Boolean) as Provider[];
+
+  providerCache.push(...providers);
+  return providers;
 }
+
+function tryInitProvider(init: () => Provider | false) {
+  try {
+    return init();
+  } catch (error) {
+    console.error('Failed to initialize provider:', error);
+    return false;
+  }
+}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 84135a2 and f453d93.

📒 Files selected for processing (10)
  • apps/web/declarations/next-auth.d.ts (2 hunks)
  • apps/web/src/app/[locale]/layout.tsx (2 hunks)
  • apps/web/src/auth/helpers/get-optional-providers.ts (1 hunks)
  • apps/web/src/auth/helpers/is-email-blocked.ts (1 hunks)
  • apps/web/src/auth/legacy/next-auth-cookie-migration.ts (1 hunks)
  • apps/web/src/auth/middleware.ts (1 hunks)
  • apps/web/src/auth/session-provider.tsx (0 hunks)
  • apps/web/src/middleware.ts (1 hunks)
  • apps/web/src/next-auth.ts (1 hunks)
  • apps/web/src/trpc/routers/auth.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • apps/web/src/auth/session-provider.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/src/trpc/routers/auth.ts
🧰 Additional context used
🧠 Learnings (1)
apps/web/src/auth/helpers/is-email-blocked.ts (1)
Learnt from: lukevella
PR: lukevella/rallly#1189
File: apps/web/src/utils/auth.ts:202-202
Timestamp: 2024-11-12T09:08:14.221Z
Learning: The `isEmailBlocked()` function in `apps/web/src/utils/auth.ts` is not asynchronous.
🪛 Biome (1.9.4)
apps/web/src/auth/middleware.ts

[error] 12-12: void is confusing inside a union type.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)


[error] 13-13: void is confusing inside a union type.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)

apps/web/src/auth/legacy/next-auth-cookie-migration.ts

[error] 20-20: void is confusing inside a union type.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)

⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: Unit tests
  • GitHub Check: Linting
  • GitHub Check: Type check
  • GitHub Check: Integration tests
🔇 Additional comments (9)
apps/web/src/auth/middleware.ts (2)

1-7: LGTM! Follows next-auth v5 best practices.

The setup correctly follows the recommended patterns for next-auth v5, using the new auth function initialization approach.


9-15: LGTM! Well-structured middleware composition.

The withAuth HOC correctly composes authentication with custom middleware while maintaining type safety. The auth() function is properly used to inject authentication data into the request chain.

🧰 Tools
🪛 Biome (1.9.4)

[error] 12-12: void is confusing inside a union type.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)


[error] 13-13: void is confusing inside a union type.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)

apps/web/declarations/next-auth.d.ts (1)

30-32: LGTM! Type declarations align with next-auth v5.

The new NextAuthRequest interface correctly extends NextRequest and includes the auth property with the appropriate type for next-auth v5.

apps/web/src/auth/legacy/next-auth-cookie-migration.ts (1)

7-10: LGTM! Secure cookie configuration based on environment.

The secure cookie configuration correctly uses the NEXT_PUBLIC_BASE_URL environment variable to determine HTTPS usage.

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

11-42: LGTM! Middleware correctly updated for next-auth v5.

The middleware implementation:

  • Properly uses the new auth migration wrapper
  • Correctly checks authentication using req.auth
  • Successfully integrates PostHog tracking
apps/web/src/app/[locale]/layout.tsx (1)

7-7: LGTM! Layout correctly updated for next-auth v5.

The changes properly implement next-auth v5's session handling:

  • Correctly imports SessionProvider from next-auth/react
  • Successfully migrates from getServerSession to auth()

Also applies to: 12-12, 33-33

apps/web/src/next-auth.ts (2)

18-23: LGTM! Well-defined schema for session updates.

The Zod schema is well-structured with appropriate types and nullish modifiers for optional fields.


35-42: LGTM! Clean providers configuration.

The providers setup is well-structured with:

  • Clear separation of static and conditional providers
  • Proper type assertion for filtered providers
  • Clean Boolean filtering approach
apps/web/src/auth/helpers/get-optional-providers.ts (1)

1-5: LGTM!

The imports are well-organized and correctly typed.

Comment on lines +28 to +33
migrateData: async (userId) => {
const session = await auth();
if (session?.user && session.user.email === null) {
await mergeGuestsIntoUser(userId, [session.user.id]);
}
},
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Potential circular dependency in adapter configuration.

The migrateData function calls auth() which is being defined in the same configuration. This circular dependency could lead to initialization issues.

Consider refactoring to use a direct database query or pass the session as a parameter to migrateData:

-    migrateData: async (userId) => {
-      const session = await auth();
-      if (session?.user && session.user.email === null) {
-        await mergeGuestsIntoUser(userId, [session.user.id]);
-      }
-    },
+    migrateData: async (userId, session) => {
+      if (session?.user && session.user.email === null) {
+        await mergeGuestsIntoUser(userId, [session.user.id]);
+      }
+    },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
migrateData: async (userId) => {
const session = await auth();
if (session?.user && session.user.email === null) {
await mergeGuestsIntoUser(userId, [session.user.id]);
}
},
migrateData: async (userId, session) => {
if (session?.user && session.user.email === null) {
await mergeGuestsIntoUser(userId, [session.user.id]);
}
},

Comment on lines +1 to +19
export const isEmailBlocked = (email: string) => {
if (process.env.ALLOWED_EMAILS) {
const allowedEmails = process.env.ALLOWED_EMAILS.split(",");
// Check whether the email matches enough of the patterns specified in ALLOWED_EMAILS
const isAllowed = allowedEmails.some((allowedEmail) => {
const regex = new RegExp(
`^${allowedEmail
.replace(/[.+?^${}()|[\]\\]/g, "\\$&")
.replaceAll(/[*]/g, ".*")}$`,
);
return regex.test(email);
});

if (!isAllowed) {
return true;
}
}
return false;
};
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add input validation and optimize performance.

The function logic is correct, but consider these improvements:

  1. Add input validation for the email parameter.
  2. Cache compiled regular expressions.
  3. Validate and sanitize environment variable format.
  4. Trim whitespace from input email and patterns.

Here's a suggested implementation:

+const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+const regexCache = new Map<string, RegExp>();
+
+function getOrCreateRegex(pattern: string): RegExp {
+  if (!regexCache.has(pattern)) {
+    const regex = new RegExp(
+      `^${pattern
+        .trim()
+        .replace(/[.+?^${}()|[\]\\]/g, "\\$&")
+        .replaceAll(/[*]/g, ".*")}$`
+    );
+    regexCache.set(pattern, regex);
+  }
+  return regexCache.get(pattern)!;
+}
+
 export const isEmailBlocked = (email: string) => {
+  if (!email || typeof email !== "string") {
+    throw new Error("Invalid email parameter");
+  }
+
+  const sanitizedEmail = email.trim().toLowerCase();
+  if (!EMAIL_REGEX.test(sanitizedEmail)) {
+    throw new Error("Invalid email format");
+  }
+
   if (process.env.ALLOWED_EMAILS) {
-    const allowedEmails = process.env.ALLOWED_EMAILS.split(",");
+    const allowedEmails = process.env.ALLOWED_EMAILS
+      .split(",")
+      .map((pattern) => pattern.trim())
+      .filter(Boolean);
+
+    if (allowedEmails.length === 0) {
+      console.warn("ALLOWED_EMAILS is set but empty or invalid");
+      return false;
+    }
+
     // Check whether the email matches enough of the patterns specified in ALLOWED_EMAILS
     const isAllowed = allowedEmails.some((allowedEmail) => {
-      const regex = new RegExp(
-        `^${allowedEmail
-          .replace(/[.+?^${}()|[\]\\]/g, "\\$&")
-          .replaceAll(/[*]/g, ".*")}$`,
-      );
-      return regex.test(email);
+      return getOrCreateRegex(allowedEmail).test(sanitizedEmail);
     });

     if (!isAllowed) {
       return true;
     }
   }
   return false;
 };

Let's verify the email validation regex:

✅ Verification successful

🌐 Web query:

What is the most reliable regex pattern for validating email addresses in TypeScript?

💡 Result:

To validate email addresses in TypeScript effectively, use a regex pattern that balances simplicity with coverage of common cases while acknowledging inherent limitations in catching all edge cases. Here are the key approaches:

Recommended Regex Patterns

1. Basic yet effective pattern
/^[^\s@]+@[^\s@]+\.[^\s@]+$/

  • Structure:
    • Prohibits spaces (^\s)
    • Requires @ and . in the domain
    • Matches common formats like [email protected]
  • Example:
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    const isValid = emailRegex.test("[email protected]"); // true

2. HTML5-compliant pattern
/^[a-zA-Z0-9.!#$%&'*+/=?^_{|}~-]+@[a-zA-Z0-9-]+(?:.[a-zA-Z0-9-]+)*$/`

  • Follows browser-native validation standards used in <input type="email"> [9].

3. Extended character support
/^[A-Za-z0-9_!#$%&'*+/=?{|}~^.-]+@[A-Za-z0-9.-]+$/`

  • Allows special characters like !, #, %, etc., in the local part [8].

Key Considerations

  1. No perfect regex exists:

    • RFC 5322 standards allow complex formats (e.g., quotes, comments), but these are rarely used and often excluded from regex patterns [2][3].
    • Example edge cases not covered by basic patterns:
      • " "@example.org (quoted local part)
      • user@[IPv6:2001:db8::1] (IP addresses in domain)
  2. Implementation best practices:

    • Test extensively: Validate against both valid (e.g., [email protected]) and invalid (e.g., invalid.email, user@domain) cases.
    • Combine with server-side validation: Regex alone cannot verify deliverability or domain existence [3][5].
    • Use libraries/services: For critical applications, consider tools like validator.js or APIs like AbstractAPI [2][5].

TypeScript Example

function validateEmail(email: string): boolean {
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return emailRegex.test(email);
}

// Usage
console.log(validateEmail("[email protected]")); // true
console.log(validateEmail("invalid.email")); // false

For most projects, the basic pattern (^[^\s@]+@...) offers a pragmatic balance between readability and effectiveness while avoiding common invalid formats.

Citations:


Input Validation & Caching Improvements Verified

The web query confirms that the basic regex pattern (/^[^\s@]+@[^\s@]+\.[^\s@]+$/) used in the refactored code is effective for typical email addresses. The improvements to validate, trim, cache regexes, and sanitize environment variables enhance the function without changing its core logic.

  • Added rigorous input checks (type verification, trimming, and pattern matching).
  • Caching the compiled regex improves performance.
  • Environment variable parsing now sanitizes each pattern.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const isEmailBlocked = (email: string) => {
if (process.env.ALLOWED_EMAILS) {
const allowedEmails = process.env.ALLOWED_EMAILS.split(",");
// Check whether the email matches enough of the patterns specified in ALLOWED_EMAILS
const isAllowed = allowedEmails.some((allowedEmail) => {
const regex = new RegExp(
`^${allowedEmail
.replace(/[.+?^${}()|[\]\\]/g, "\\$&")
.replaceAll(/[*]/g, ".*")}$`,
);
return regex.test(email);
});
if (!isAllowed) {
return true;
}
}
return false;
};
const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const regexCache = new Map<string, RegExp>();
function getOrCreateRegex(pattern: string): RegExp {
if (!regexCache.has(pattern)) {
const regex = new RegExp(
`^${pattern
.trim()
.replace(/[.+?^${}()|[\]\\]/g, "\\$&")
.replaceAll(/[*]/g, ".*")}$`
);
regexCache.set(pattern, regex);
}
return regexCache.get(pattern)!;
}
export const isEmailBlocked = (email: string) => {
if (!email || typeof email !== "string") {
throw new Error("Invalid email parameter");
}
const sanitizedEmail = email.trim().toLowerCase();
if (!EMAIL_REGEX.test(sanitizedEmail)) {
throw new Error("Invalid email format");
}
if (process.env.ALLOWED_EMAILS) {
const allowedEmails = process.env.ALLOWED_EMAILS
.split(",")
.map((pattern) => pattern.trim())
.filter(Boolean);
if (allowedEmails.length === 0) {
console.warn("ALLOWED_EMAILS is set but empty or invalid");
return false;
}
// Check whether the email matches enough of the patterns specified in ALLOWED_EMAILS
const isAllowed = allowedEmails.some((allowedEmail) => {
return getOrCreateRegex(allowedEmail).test(sanitizedEmail);
});
if (!isAllowed) {
return true;
}
}
return false;
};

@vercel vercel bot temporarily deployed to Preview – landing February 12, 2025 05:43 Inactive
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/middleware.ts (1)

9-15: ⚠️ Potential issue

Remove unsafe type assertion.

The undefined as never type assertion in the auth function call is unsafe and might indicate a type mismatch. Based on the next-auth v5 documentation, the second parameter is not required.

Apply this diff to remove the type assertion:

-  return (req: NextRequest) => auth(middleware)(req, undefined as never);
+  return (req: NextRequest) => auth(middleware)(req);
🧰 Tools
🪛 Biome (1.9.4)

[error] 12-12: void is confusing inside a union type.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)


[error] 13-13: void is confusing inside a union type.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)

🧹 Nitpick comments (1)
apps/web/src/middleware.ts (1)

16-16: Consider using a more explicit authentication check.

The current check req.auth?.user?.email might allow users with empty email strings. Consider using a more explicit check.

-    const isLoggedIn = req.auth?.user?.email;
+    const isLoggedIn = Boolean(req.auth?.user?.email);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f453d93 and dcaae00.

📒 Files selected for processing (4)
  • apps/web/declarations/next-auth.d.ts (2 hunks)
  • apps/web/src/auth/legacy/next-auth-cookie-migration.ts (1 hunks)
  • apps/web/src/auth/middleware.ts (1 hunks)
  • apps/web/src/middleware.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/declarations/next-auth.d.ts
🧰 Additional context used
🪛 Biome (1.9.4)
apps/web/src/auth/legacy/next-auth-cookie-migration.ts

[error] 20-20: void is confusing inside a union type.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)

apps/web/src/auth/middleware.ts

[error] 12-12: void is confusing inside a union type.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)


[error] 13-13: void is confusing inside a union type.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Integration tests
🔇 Additional comments (5)
apps/web/src/middleware.ts (2)

11-12: LGTM! Clean middleware composition.

The middleware composition using withAuthMigration and withAuth is well-structured, ensuring proper handling of both legacy and new authentication flows.


37-39: LGTM! Proper PostHog integration.

The PostHog integration is correctly implemented, using the user's ID as the distinct identifier.

apps/web/src/auth/legacy/next-auth-cookie-migration.ts (3)

53-58: LGTM! Secure cookie configuration.

The cookie is properly configured with secure attributes:

  • Path restricted to "/"
  • Secure flag based on HTTPS
  • SameSite policy set to "lax"
  • HttpOnly flag enabled

7-9: Verify NEXT_PUBLIC_BASE_URL configuration.

The NEXT_PUBLIC_BASE_URL environment variable is used to determine cookie security. Ensure this variable is properly configured in all environments.

❌ Verification inconclusive

🏁 Script executed:

#!/bin/bash
# Description: Check for NEXT_PUBLIC_BASE_URL in environment files

echo "Checking environment files for NEXT_PUBLIC_BASE_URL..."
fd -t f "^\.env" -x grep -l "NEXT_PUBLIC_BASE_URL"

Length of output: 377


Below is an updated script that adjusts the search to include hidden files. This will help verify that the NEXT_PUBLIC_BASE_URL key is present in your environment configuration files (typically named like .env, .env.local, etc.):

Next Steps:

  • Run the above script.
  • Verify that the output lists the appropriate .env files.
  • Check that the NEXT_PUBLIC_BASE_URL is correctly set in these files across all environments (development, staging, production) so that secure cookies are enabled in production.

🏁 Script executed:

[run_scripts]
#!/bin/bash
echo "Checking hidden environment files for NEXT_PUBLIC_BASE_URL..."
fd -t f -H "^\.env" -x grep -l "NEXT_PUBLIC_BASE_URL"

Length of output: 348


NEXT_PUBLIC_BASE_URL is Defined—but Verify Production Setup

The search shows that the variable is currently defined in your test and development config files (i.e. in ./apps/web/.env.test and ./.env.development). However, there isn’t any indication of a production environment file (such as .env.production or a .env file) in the repository. Please make sure that your production environment is either configured with the appropriate HTTPS URL (to ensure cookies are marked secure) or that the variable is set externally as needed.


46-51: Verify SECRET_PASSWORD configuration.

The JWT encoding uses SECRET_PASSWORD for signing. Ensure this secret is properly configured and has sufficient entropy.

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Description: Check for SECRET_PASSWORD in environment files

echo "Checking environment files for SECRET_PASSWORD..."
fd -t f "^\.env" -x grep -l "SECRET_PASSWORD"

Length of output: 362


Below is an updated script that includes the hidden file flag to search for .env files. Please run it to check for the existence and configuration of SECRET_PASSWORD:

Action: Verify SECRET_PASSWORD in Environment

The original command did not search hidden files, so it couldn’t list files like .env or .env.local that may contain the secret. Once you run the revised script, please verify that the SECRET_PASSWORD variable is set in one of those environment files and that its value is appropriately strong (i.e., has sufficient entropy) for signing JWTs.


🏁 Script executed:

[run_scripts]
#!/bin/bash
echo "Checking hidden environment files for SECRET_PASSWORD using --hidden flag..."
fd --hidden -t f "^\.env" -x grep -l "SECRET_PASSWORD" || echo "No .env file with SECRET_PASSWORD found."

Length of output: 379


Action: Verify SECRET_PASSWORD Configuration and Entropy in Environment Files
We confirmed that the SECRET_PASSWORD variable is present in two environment files:

  • ./.env.development
  • ./apps/web/.env.test

However, the review comment is also concerned with ensuring that the secret used for JWT signing has sufficient entropy. Please verify that the actual values in these files meet security best practices. In particular, check that:

  • The value is generated with cryptographically strong randomness, and
  • It has a sufficient length (typically 32+ characters is recommended).

To help with this, you might run the following script to check the length of the secret values in each file:

Please run the above script and verify that the SECRET_PASSWORD values are adequately strong for production use.


🏁 Script executed:

[run_scripts]
#!/bin/bash
echo "Checking SECRET_PASSWORD lengths in environment files..."
for file in ./.env.development ./apps/web/.env.test; do
  if [ -f "$file" ]; then
    echo "Processing $file:"
    grep "SECRET_PASSWORD=" "$file" | while IFS='=' read -r var secret; do
      # Trim any spaces and potential quotes
      secret=$(echo "$secret" | tr -d ' "\'')
      len=${#secret}
      echo "  SECRET_PASSWORD length: $len"
      if [ "$len" -lt 32 ]; then
        echo "  WARNING: The secret in $file may be too short for sufficient entropy."
      fi
    done
  else
    echo "File $file not found."
  fi
done

Length of output: 361


Below is an updated script that fixes the quoting issues so you can properly verify the lengths (and thus the likely entropy) of the SECRET_PASSWORD values in your environment files:

Action: Verify SECRET_PASSWORD Entropy

We confirmed that two environment files contain SECRET_PASSWORD (in ./.env.development and ./apps/web/.env.test). Please run the updated script to check that the secret lengths are at least 32 characters—as a proxy for sufficient entropy for JWT signing. Adjust the secret values if they fall short of this recommendation.


🏁 Script executed:

[run_scripts]
#!/bin/bash
echo "Checking SECRET_PASSWORD lengths in environment files..."
for file in ./.env.development ./apps/web/.env.test; do
  if [ -f "$file" ]; then
    echo "Processing $file:"
    grep "SECRET_PASSWORD=" "$file" | while IFS='=' read -r var secret; do
      # Remove double quotes
      secret=$(echo "$secret" | tr -d '"')
      # Remove single quotes
      secret=$(echo "$secret" | tr -d "'")
      len=${#secret}
      echo "  SECRET_PASSWORD length in $file: $len"
      if [ "$len" -lt 32 ]; then
        echo "  WARNING: The secret in $file may be too short for sufficient entropy."
      fi
    done
  else
    echo "File $file not found."
  fi
done

Length of output: 1434


SECRET_PASSWORD Verification

Both the ./.env.development and ./apps/web/.env.test files were found to include a SECRET_PASSWORD value, and the secret’s length is exactly 32 characters in both cases. This meets the recommended minimum length requirement for sufficient entropy in JWT signing.

No further action is required regarding the secret configuration.

@lukevella lukevella merged commit 4b26dc5 into main Feb 12, 2025
10 checks passed
@lukevella lukevella deleted the authjs branch February 12, 2025 05:51
lukevella added a commit that referenced this pull request Feb 12, 2025
🐛 Update name in session (#1559)
lukevella added a commit that referenced this pull request Feb 12, 2025
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.

1 participant