authenticationnext.jsnext-authauth.js

Problem with redirecting the user after sucessful login on Next.js with Next-auth v5


I'm making an app using Next.js 14.2.5, tRPC and Next-auth 5.0.0-beta.20 (or Auth.js) for the authentication. After the user logs in the app i want to redirect him to another page, however, when i try to do that a NEXT_REDIRECT error appears and the app does nothing. For what i've read this error is expected and you are supposed to throw it at the end of the catch block so next.js can continue with the redirect but that does not work.

This is the code of the tRPC endpoint that i use for the login:

loginProcedure: publicProcedure.input(LoginSchema).mutation(async (opts) => {
    const { input } = opts;
    const validatedFields = LoginSchema.safeParse(input);
    if (!validatedFields.success) {
      throw new TRPCError({ code: "PARSE_ERROR" }); //"invalid fields"
    }
    const { email, password } = validatedFields.data;
    try {
      await signIn("credentials", {
        email,
        password,
        redirectTo: DEFAULT_LOGIN_REDIRECT,
      });
    } catch (error) {
      if (error instanceof AuthError) {
        switch (error.type) {
          case "CredentialsSignin":
            throw new TRPCError({
              code: "UNAUTHORIZED",
              message: "Invalid credentials",
            });

          default:
            throw new TRPCError({
              code: "UNAUTHORIZED",
              message: "Something went wrong",
            });
        }
      }
      console.log(error)
      throw error;
    }
  }),  

And this is my middleware.ts

import { auth } from "@/auth";
import {
  DEFAULT_LOGIN_REDIRECT,
  apiAuthPrefix,
  authRoutes,
  publicRoutes,
} from "@/routes";

export default auth((req) => {
  const { nextUrl } = req;
  const isLoggedIn = !!req.auth;

  const isApiAuthRoute = nextUrl.pathname.startsWith(apiAuthPrefix);
  const isPublicRoute = publicRoutes.includes(nextUrl.pathname);
  const isAuthRoute = authRoutes.includes(nextUrl.pathname);

  if (isApiAuthRoute) {
    return ;
  }
  if(isAuthRoute){
    if (isLoggedIn){
      return Response.redirect(new URL(DEFAULT_LOGIN_REDIRECT, nextUrl))
    }
    return ;
  }
  if(!isLoggedIn && !isPublicRoute){
    return Response.redirect(new URL("/login", nextUrl))
  }
});

export const config = {
  matcher: [
    // Skip Next.js internals and all static files, unless found in search params
    "/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)",
    // Always run for API routes
    "/(api|trpc)(.*)",
  ],
};

If i put the correct credentials the login goes without issue and i can fetch the current session but the redirect just never happens. Is this a Next.js bug or i'm missing something?

I've also tried several solutions, mainly the ones in this github thread.


Solution

  • I created an action.ts file with the following code:

    "use server";
    
    export async function doCredentialLogin(formData: FormData) {
      try {
        const response = await signIn("credentials", {
          email: formData.get("email"),
          password: formData.get("password"),
          redirect: false,
        });
    
        revalidatePath("/");
        return response;
      } catch (err) {
        console.error("*** doCredentialLogin Error ***", err);
        return err;
      }
    }

    Then, in my Login Form component (LoginForm.tsx), I have the following code:

    "use client";
    
    import { doCredentialLogin } from "@/app/actions";
    import { useRouter } from "next/navigation";
    // ...
    
    const router = useRouter();
    
    // ...
    const formData = new FormData();
    formData.append("email", email);
    formData.append("password", password);
    await doCredentialLogin(formData);
    // After successful login, redirect the user
    router.push("/dashboard");