Next.js Discord

Discord Forum

Protected client side routes flashing the protected content before redirecting to login page.

Unanswered
GuitarNerd posted this in #help-forum
Open in Discord
Hi everyone,

I'm building a Next.js app with Laravel on the backend. I'm experienced in React but new to Next.js.
In order to protect the routes I've added the following Context.

import React, { useEffect } from "react";
import  { useRouter } from "next/navigation";
import { getCookie, setCookie } from "cookies-next";
import { usePathname } from "next/navigation";

const AuthContext = React.createContext();
const { Provider } = AuthContext;

const AuthProvider = ({ children }) => {
  const [authState, setAuthState] = React.useState();
  const pathname = usePathname();
  const router = useRouter();
  let restrictedRoutes = ['/created', '/waiting', '/'];
  useEffect(() => {
    const user = getCookie('user');
    setAuthState(user);
  }, [])
  useEffect(() => {
    if (authState) {
      setCookie('user', authState);
    }
  }, [authState]);

  useEffect(() => {
    // console.log('userrrr', authState);
    // if (authState) {
    //   console.log('userrrr', JSON.parse(authState));
    // }
    if (!authState && restrictedRoutes.includes(pathname)) {
      router.push('/login');
    } else {
      if (authState && restrictedRoutes && !JSON.parse(authState).user.email_verified_at) {
        router.push('/verification/' + JSON.parse(authState).user.email);
      }
    }
  }, [pathname, authState])
  // checks if the user is authenticated or not
  const isUserAuthenticated = () => {
    if (!authState.token) {
      return false;
    }
  };

  return (
    <Provider
      value={{
        authState,
        setAuthState,
        isUserAuthenticated,
      }}
    >
      {children}
    </Provider>
  );
};

export { AuthContext, AuthProvider };

24 Replies

I also have had to wrap it in a wrapper to avoid errors before using it in the layout.


"use client"
import { AuthContext, AuthProvider } from "@/contexts/AuthContext";

interface Props {
    children: React.ReactNode;
}

const AuthContextWrapper: React.FC = ({ children }) => {
    return (
        <AuthProvider value={{
            authState: null,
            setAuthState: null,
            isUserAuthenticated: null,
        }}>
            {children}
        </AuthProvider>
    )
}


export default AuthContextWrapper


In the layout I've used it as such

const RootLayout: React.FC<Props> = ({ children }) => {

  return (
    <html lang="en">
      <body className={styles.body}>
        <AuthContextWrapper>
            <section className={styles.formSection}>
              <Image src="/images/logo.svg" alt="logo" width={182} height={26} className={styles.mobileLogo} />
              {children}
            </section>
        </AuthContextWrapper>
      </body>
    </html >
  )
}


What am I doing wrong ?
Use middleware for your protected routes instead of useEffect
@davdixyz for client routing ?
For example:
middleware.ts
import { NextResponse } from "next/server";
import { NextRequest } from "next/server";

export function middleware(request: NextRequest) {
  const user = request.cookies.get("user");
  const restrictedRoutes = ["/created", "/waiting"];
  if (!user && restrictedRoutes.includes(request.nextUrl.pathname)) {
    request.nextUrl.pathname = "/login"; // where you want to redirect if the cookie does not exist
    return NextResponse.redirect(request.nextUrl);
  }
  return NextResponse.next();
}
Excuse me, @davdixyz ,
I am facing exactly the same problem and tried to use middleware but have not solved it yet.
Apart from creating this middleware file, can you, please, tell me what else I need to do to make it work 🙏 ?
No, I am still using the pages directory.
And I have the middleware file in the pages directory
Can you send your current code? I can't really find in the forum
Sure, which file do you want me to send? Login, or the middleware?
Your middleware
Sure, just a moment
Here you go:
import useUserStore from "@store/user";
import { NextResponse } from "next/server";
import { NextRequest } from "next/server";

export function middleware(request: NextRequest) {
const { loggedIn } = useUserStore();
const restrictedRoutes = ["/dashboard", "/test"];

console.log('loggedIn::::::', loggedIn);

if (!loggedIn && restrictedRoutes.includes(request.nextUrl.pathname)) {
request.nextUrl.pathname = "/login";
return NextResponse.redirect(request.nextUrl.pathname);
}

return NextResponse.next();
}

export const config = { matcher: "/dashboard", };
Do we need to import this file somewhere that we export it here? In case that's the reason it doesn't work.
And what if I don't use the src directory, but instead I use the pages directory right away like what you can see in this screenshot:
Try with this middleware:
export function middleware(request: NextRequest) {
  const { loggedIn } = useUserStore()

  if (request.nextUrl.pathname.startsWith("/_next")) return NextResponse.next();

  if (!loggedIn) {
    request.nextUrl.pathname = "/login";
    return NextResponse.redirect(request.nextUrl);
  }

  return NextResponse.next();
}
export const config = {
  matcher: ["/dashboard/:path*"],
};
Thank you.
Is the place of the file okay?
yeah
The thing is, I tried to console.log something on top of this file but I could not see it on the console. And I was wondering if this middleware function is even triggered. What do you think?
If you export a matcher the middleware only triggers on the specified paths
In the same file or in a specific file?
here
Yes, I already have that exported.
Any other ideas why this isn't working?