Next.js Discord

Discord Forum

Middleware doesn't redirect to /dashboard

Unanswered
Skipjack tuna posted this in #help-forum
Open in Discord
Avatar
Skipjack tunaOP
Hey guys, so my problem is that after my user logged in successfully and gets his JWT set in the browser cookies, Next should redirect him to his /dashboard page. But that doesn't happen, despite the login being successfully fulfilled, Next stays on the same route. In the frontend, I made sure to use "router.push("/dashboard") once the fetch succeeded, and in my middleware, I have the following code:

import { NextResponse } from "next/server";


export const middleware = (request) => {
  const path = request.nextUrl.pathname;
  console.log(path)

  const isPublicPath = path === "/login" || path === "/register" || path === "/"

  const token = request.cookies.get("token")?.value || "";

  if (isPublicPath && token && path !== "/") {
    console.log("PUBLIC PATH ACCESSED")
    return NextResponse.redirect(new URL("/", request.nextUrl));
  }

  if (!isPublicPath && token) { //this should make the user get redirected to the dashboard, as it isnt a public path, and yet he already has his token!
    return NextResponse.next()
  }

  if (!isPublicPath && !token) {
    console.log("NON-PUBLIC PATH ACCESSED")
    return NextResponse.redirect(new URL("/login", request.nextUrl));
  }
};

export const config = {
  matcher: ["/", "/dashboard", "/login", "/register"],
};


I even commented the line for you which is supposed to redirect the user successfully.
Also sometimes, even though some other redirections work, the Browser URL doesn't change - why??
Thank you guys!

EDIT: When I change
if (!isPublicPath && token) { 
    return NextResponse.next()
  }


to
if (isPublicPath == false && token) { 
    return NextResponse.next()
  }


it works (sometimes I have to click twice on it though, wtf!?). HOW? 😩

41 Replies

Avatar
Skipjack tunaOP
bumping this...
Avatar
Skipjack tunaOP
bump...
Avatar
Skipjack tunaOP
bump
Avatar
SupremeDeity
Before i check any logic.
Is the Middleware in the same directory as package.json?
Or if you are using src folder, is it in src folder, meaning on the same level as app folder? (Not inside app folder, on the same level)
Avatar
Skipjack tunaOP
Hey, no I am not using the src folder and therefore, I put my middleware.js in the project folder, its on the same level as the package.json / app folder / etc
I defined all my pages as "use client" pages btw, maybe that helps?

If necessary, I can also provide the part of the frontend code, where I push the user to the /dashboard page.
I noticed that it works SOMETIMES, and sometimes (mostly the first time) NOT. So weird...
Avatar
Skipjack tunaOP
bump @SupremeDeity
Avatar
Asian black bear
Hey can did you check if you are getting the token ?
you can check by logging it in the console.
Btw there is a specific way do get token.
import:
import { getToken } from "next-auth/jwt";

const token = await getToken({ req, secureCookie: process.env.NODE_ENV === "production", });

Plus dont forget to add async when initialisation on the middleware function
Avatar
Pacific herring
If you need to click twice (click what, login button or something?) to be redirected to the dashboard, iit's because you may not have the token created at the time you click login, therefore it doesn't go through your condition
you feel me?
If you give the cookie when the user logs in, if you're using 0auth you can give in the callback
        async signIn({ user, account, profile, email, credentials }) {
            cookies().set('mytoken', user.id);
            }
@Skipjack tuna
Avatar
Asian black bear
when you click the login button. you may be calling signIn from next-auth. If yes, you can give the callback url with that. So, when the login will be successful it will redirect automatically you will not have to do it manually.
signIn(credentials, { email: values.email, callbackUrl: "/dashboard", redirect: true, });
Avatar
Skipjack tunaOP
Hey guys @Asian black bear @Pacific herring unfortunately, it is still not working. However, thanks for the help already, I hope we can still solve this together!! 🙂

1. I changed my middleware function to async now. I thought it didnt need async because the next js documentation also wrote it without "async"!?

2. The thing is: I am not using Next Auth! I implemented everything by myself from scratch (code below). Can I still use the functions you showed me? Probably not, I guess...

3. I figured out exactly WHEN the problem happens: If I go to /login and log in, then it perfectly redirects me to my /dashboard. BUT if I am logged out, first go to /dashboard (which correctly redirects me to /login), and then enter my login data - THEN THIS IS where the error happens and it doesnt redirect me. Then I need to manually go to /dashboard...

The token gets set correctly I believe, its in my Cookies in the browser (I can see it there). I created a manual helper function before to get the cookie, is this okay?

import Jwt from "jsonwebtoken";

export const getDataFromToken = (request) => {
  try {
    const token = request.cookies.get("token")?.value || "";
    const decodedToken = Jwt.verify(token, process.env.JWT_SECRET);
    return decodedToken.id;
  } catch (error) {
    console.log(error);
  }
};
And here is my API Endpoint for /login

import { connectMongo } from "@/utils/connectMongo";
import Info from "@/models/userModel";
import bcryptjs from "bcryptjs";
import { NextResponse } from "next/server";
import Jwt from "jsonwebtoken";

export const POST = async (req) => {
  const { email, password } = await req.json();

  try {
    await connectMongo();

    //check if user exists
    const user = await Info.findOne({ email });
    if (!user) {
      return new Response("User does not exist!", { status: 400 });
    }

    //compare passwords
    const validPassword = await bcryptjs.compare(password, user.password);
    if (!validPassword) {
      return new Response("Invalid password!", { status: 401 });
    }

    //create token data
    const tokenData = {
      id: user._id,
      username: user.username,
      email: user.email,
    };

    //create jwt token
    const token = Jwt.sign(tokenData, process.env.JWT_SECRET, {
      expiresIn: "1h",
    });

    const response = NextResponse.json({
      message: "Login successful",
      success: true,
    });
    response.cookies.set("token", token, {
      httpOnly: true,
    });

    return response;
  } catch (error) {
    console.log(error);
    return new Response("Error! Something went wrong.", { status: 500 });
  }
};


@Asian black bear @Pacific herring
Avatar
Pacific herring
Can you send your whole Middleware please and also what is in the login form or wherever you do the request
Avatar
Skipjack tunaOP
@Pacific herring Sure! Whole middleware:

import { NextResponse } from "next/server";

export const middleware = async (request) => {
  const path = request.nextUrl.pathname;
  console.log(path);

  const isPublicPath =
    path === "/login" || path === "/register" || path === "/";

  const token = request.cookies.get("token")?.value || "";

  if (isPublicPath && token && path !== "/") {
    return NextResponse.redirect(new URL("/", request.nextUrl));
  }

  if (!isPublicPath && token) {
    //this should make the user get redirected to the dashboard, as it isnt a public path, and yet he already has his token!
    return NextResponse.next();
  }

  if (!isPublicPath && !token) {
    return NextResponse.redirect(new URL("/login", request.nextUrl));
  }
};

export const config = {
  matcher: ["/", "/dashboard", "/login", "/register"],
};



/login page.jsx where I do the req:
Discord only lets me upload the login page jsx as a file, sorry for that
But yeah, there you go @Pacific herring
Image
Avatar
Pacific herring
sorta mess xd
we need a lil debug here so we understand what's really going on
i'd start logging in every condition
also, if i were you
i'd use this one
to build forms
ACTUALLY
i'd rewrite the whole form with react hook form
Avatar
Skipjack tunaOP
Good idea, I can do that, would be much less code lol
But where should I debug my middleware problem? Im a little lost cause I still dont understand why this is not working @Pacific herring
Avatar
Pacific herring
i dont think the problem is in your middleware
Avatar
Skipjack tunaOP
maybe its only in the handleSubmit function in the frontend then. But even there, I dont really see a wild issue; all I know is that I use "router.push("/dashboard") if the API response == ok... Here, I removed all the additional features and only left the raw fetch request for you, check this out @Pacific herring

const handleSubmit = async (e) => {
    e.preventDefault();
    try {
      const response = await fetch("/api/users/login", {
        method: "POST",
        body: JSON.stringify({
          email: loginData.email,
          password: loginData.password,
        }),
        headers: {
          "Content-Type": "application/json",
        },
      });

      if (response.ok) {
        console.log("Response for POST request was OK! :)");
        router.push("/dashboard");
      }
    } catch (error) {
      console.log(error);
    } finally {
      setIsLoggingIn(false);
    }
  };
Avatar
Skipjack tunaOP
bump
Avatar
Masai Lion
Can you try router.refresh or redirect https://nextjs.org/docs/app/api-reference/functions/redirect this one?
The issue you are facing is a bit tricky since it happens only if you are logged out and navigate from /dashboard to /login . I suspect some middleware issue with your code. Does the console.log(path) gets triggered when you do that specific flow ? I might setup codesanbox container to reproduce your stuff and post it here later if I find something that might help ya
Avatar
Masai Lion
Okay after some testing everything is working 🙂 So the issue is somewhere with your auth logic that handles the Token ... keep in mind you can not use Crypto(JWT package ) in the middleware.js/ts since it supports only node js runtime and the middleware is using edge runtime
Avatar
Masai Lion
https://hvz8qp-3000.csb.app/ <-the codesandbox example heres the code https://github.com/gadaotada/redirects-issues-next keep in mind this doesnt have any auth logic so to recreate the redirects you have to remove the token cookie manually
Avatar
Skipjack tunaOP
@Masai Lion I've sent you a DM with a question regarding your solution! 🙂