nextauthjs / next-auth

Authentication for the Web.
https://authjs.dev
ISC License
25.06k stars 3.54k forks source link

Nextjs13: Not redirected after signin by the middleware.ts file in the deployed version of the project. #8978

Open danishrp102 opened 1 year ago

danishrp102 commented 1 year ago

Environment

System: OS: macOS 13.0.1 CPU: (8) arm64 Apple M1 Memory: 365.48 MB / 8.00 GB Shell: 5.8.1 - /bin/zsh Binaries: Node: 18.17.1 - /usr/local/bin/node npm: 9.6.7 - /usr/local/bin/npm next: 13.4.9 -> 14.0.0 next-auth: 4.20.1 Browsers: Brave Browser: 117.1.58.137 Chrome: 118.0.5993.117 Safari: 16.1

Reproduction URL

https://github.com/danishrp102/TypeThreads

Describe the issue

So, I have used next-auth for Google authentication. For my nextjs13 project, I have created a middleware.ts file in the src directory for route protection. I have no problems with the app functioning in development, but after I deploy it to vercel, I am not able to access the dashboard after successful signIn. I can say that I am successfully signed in because the Redis database shows the user stored in the DB. Please help me to solve this issue.

How to reproduce

Head over to https://type-threads.vercel.app/ and try to signIn through your google account

OR

Visit my repository https://github.com/danishrp102/TypeThreads to clone it and run it locally. This would require you to create an upstash account, pusher account and install all the dependencies as mentioned int the package.json

OR

This is my middleware.ts file:

// prevents relogins and accessing sensitive routes without logins

import { getToken } from "next-auth/jwt";
import { withAuth } from "next-auth/middleware";
import { NextResponse } from "next/server";

export default withAuth(
    async function middleware(req) {
        const pathname = req.nextUrl.pathname;

        // Manage the route protection
        const isAuth = await getToken({ req });
        const isLoginPage = pathname.startsWith('/login');

        const sensitiveRoutes = ['/dashboard'];
        const isAccessingSensitiveRoutes = sensitiveRoutes.some((route) => pathname.startsWith(route));

        if (isLoginPage) {
            if (isAuth) {
                return NextResponse.redirect(new URL('/dashboard', req.url));
            }

            return NextResponse.next();
        }

        if (!isAuth && isAccessingSensitiveRoutes) {
            return NextResponse.redirect(new URL('/login', req.url));
        }

        if (pathname === '/') {
            return NextResponse.redirect(new URL('/dashboard', req.url));
        }
    }, {
    callbacks: {
        async authorized() {
            return true;
        },
    }
}
)

export const config = {
    matcher: ['/', '/login', '/dashboard/:path*']
}

Add this to your project and implement simple google authentication using next-auth.

Expected behavior

I should be able to successfully signIn, but I am not redirected to the dashboard. Instead, the page is refreshed while the token values are apparently not received in the middleware file in the deployed version.

emersonbroga commented 1 year ago

Im following the steps here https://nextjs.org/learn/dashboard-app/adding-authentication using the new NextJS 14 with the next-auth@beta and even after Credentials authorize return a user, it doesn't redirect to the dashboard page.

export const { auth, signIn, signOut } = NextAuth({
  ...authConfig,
  providers: [
    Credentials({
      async authorize(credentials) {
        // ...

        if (parsedCredentials.success) {
          const { email, password } = parsedCredentials.data;
          const user = await getUser(email);
          if (!user) return null;
          const passwordsMatch = await bcrypt.compare(password, user.password);

          if (passwordsMatch) return user; // <<< Passwords match and the user is returned.
        }

        console.log('Invalid credentials');
        return null;
      },
    }),
  ],
});
Kayoshi-dev commented 1 year ago

Hi! Having the same issue as @emersonbroga! To reproduce you can simply clone the next-learn final example and try to run it using a custom API.

It seems like the user on the auth object in the auth.config.ts is never populated which causing this test : const isLoggedIn = !!auth?.user; to fail and return false

theopomies commented 1 year ago

For @emersonbroga and @Kayoshi-dev, the fix is to add AUTH_URL=http://localhost:3000/api/auth to your .env, alongside AUTH_SECRET

chungweileong94 commented 1 year ago

The same issue also happen to the signOut, my workaround for sign out is to redirect manually in middleware.

But I yet to find a workaround for signIn, the only way is to use the client-side signIn from next-auth/react.

mhughdo commented 1 year ago

@Kayoshi-dev @emersonbroga

I was having the same issue but I've managed to fix the null auth object problem by removing the .next folder and add AUTH_URL to the .env file

Flavien-Pensato commented 9 months ago

I finally find out in my case.

When I signIn, in my server action, I was not throwing the error in my catch witch cause the redirectTo to not work correctly (I don't know why)

So just do that instead:

    try {
        await signIn("credentials", {
            email,
            password,
            redirectTo: LOGIN_REDIRECT,
        });
    } catch (error) {
        if (error instanceof AuthError) {
            switch (error.type) {
                case "CredentialsSignin":
                    return { error: "Invalid credentials" };
                default:
                    return { error: "Something went wrong" };
            }
        }

                // HERE
        throw error
    }
PAAPII10 commented 9 months ago

This is not the proper solution but for workaround i used window.location.reoload() way and managed my protected routes in middleware


    try {
      const response = await signIn('credentials', { ...formData, redirect: false });
      console.log(response);
      if (!response?.ok && response?.error) {
        if (response.error === 'FirstLogin') {
          const firstLoginToken = `${getRandomUUID()}-${getRandomUUID()}`.replace(/-/g, '');
          const { error } = await createToken(formData.email, firstLoginToken);
          if (error) {
            form.reset();
            setSubmitError(error);
          }
          router.replace(`/setup-password/${firstLoginToken}`);
          return;
        }
        form.reset();
        setSubmitError(response?.error);
        return;
      }
      await sleep(200);
      window.location.reload();
    } catch (error) {
      console.log(error);
    }
  };```

  If any one have better approach or solution please reply because I am facing issue
abdinasir-Tman commented 6 months ago

I finally find out in my case.

When I signIn, in my server action, I was not throwing the error in my catch witch cause the redirectTo to not work correctly (I don't know why)

So just do that instead:

  try {
      await signIn("credentials", {
          email,
          password,
          redirectTo: LOGIN_REDIRECT,
      });
  } catch (error) {
      if (error instanceof AuthError) {
          switch (error.type) {
              case "CredentialsSignin":
                  return { error: "Invalid credentials" };
              default:
                  return { error: "Something went wrong" };
          }
      }

                // HERE
      throw error
  }

Thank you man i had the same issue but after added the throw error, worked correctly