Protected client side routes flashing the protected content before redirecting to login page.
Unanswered
GuitarNerd posted this in #help-forum
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.
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.
In the layout I've used it as such
What am I doing wrong ?
"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 AuthContextWrapperIn 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.tsimport { 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 🙠?
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", };
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?
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?
Yes, I already have that exported.
Any other ideas why this isn't working?