Next.js Discord

Discord Forum

How to restrict routes if use is not logged in?

Unanswered
Tiphiid wasp posted this in #help-forum
Open in Discord
Tiphiid waspOP
Hi there!

I'm creating a dashboard where the user needs to login to access everything in the app directory.

If user is not logged in, redirect from app to login.

Now, I'm using JWT and localstorage as that's what the back-end gives me as well. Its poorly done but... I didn't do it.

So how would you go about doing this in NextJS compared to React? Is there some magical way to do it or something I need to be aware of or use some packages or something?

90 Replies

not using localStorage, use a cookie so you can check it on server side. Then make a middleware that checks your protected routes for the cookie and redirect if its invalid before anything renders.
Giant panda
Of course you can, otherwise they would be entirely pointless.
You are probably thinking of HttpOnly cookies which prevent JS access on the client
But the middleware runs on the server
And I can just +1 what KINXZ wrote, cookies + middleware is the way to go
Tiphiid waspOP
This is fully SPA appllication as well
So even if its full SPA it also cookies?
Giant panda
You don't have a SPA
It's an SSRed Next app which is a MPA
Tiphiid waspOP
I mean nothing will be SSR
basically 'use client' everywhere
Giant panda
It's still SSRed
Tiphiid waspOP
Hmm
Giant panda
Client components are still prerendered on the server.
If you really want a fully client-side SPA then Next is likely not the right tool and Vite would just be classic and more suited
Tiphiid waspOP
I'm transitioning from vite to nextjs right now xd
just trying to figure out how to do the same things I did in vite in nextjs
but okay
so you say to use a cookie with nextjs and that's osmething js can access
Giant panda
Server-side code can access cookies, yes
Tiphiid waspOP
Hmmm, okay. I'll give it a go like this and see where I end up, thanks 😄
Tiphiid waspOP
Alright, so I got this error:
:3000/api/auth/session:1     Failed to load resource: the server responded with a status of 404 (Not Found)
app-index.js:31 [next-auth][error][CLIENT_FETCH_ERROR] 
https://next-auth.js.org/errors#client_fetch_error Unexpected token '<', "<!DOCTYPE "... is not valid JSON Object
window.console.error @ app-index.js:31
:3000/api/auth/session:1     Failed to load resource: the server responded with a status of 404 (Not Found)
app-index.js:31 [next-auth][error][CLIENT_FETCH_ERROR] 
https://next-auth.js.org/errors#client_fetch_error Unexpected token '<', "<!DOCTYPE "... is not valid JSON Object
window.console.error @ app-index.js:31
:3000/api/auth/_log:1     Failed to load resource: the server responded with a status of 404 (Not Found)
:3000/api/auth/_log:1     Failed to load resource: the server responded with a status of 404 (Not Found)


Super confused what that is

Tries a few things...

I hosted NextAuth in... (screenshot) but also tried it inside app, without pages etc... etc..

And the error only appears after I add SessionProvider:
"use client";
import { useState } from "react";

import { SessionProvider } from "next-auth/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";

export default function Provider({ children }: { children: React.ReactNode }) {
  const [client] = useState(new QueryClient());

  if (process.env.NEXT_PUBLIC_VITE_ENVIRONMENT === undefined) {
    console.error("Enviroment file not defined or staging variable not defined (.env)")
  }

  return (
    <SessionProvider>

      <QueryClientProvider client={client}>
        {children}
        <ReactQueryDevtools initialIsOpen={false} position="bottom-right" />
      </QueryClientProvider>
    </SessionProvider>
  );
}


From day one basically.

Not sure what am suppsoed to do with this
Tiphiid waspOP
and even if that file doesn't exist, it will error out
something makes me feel the folder structure is wrong xd
I've also tried puttingthe api in app
tried putting pages in app
I have no idea what else there I could do
Tiphiid waspOP
alright so it seemslike for enxtj13.2+ this is the correct scttureu
so got that and still ssame error
btw 404 is now [not-found.tsx](https://nextjs.org/docs/app/api-reference/file-conventions/not-found) in app dir (unreleated, but still something)
Tiphiid waspOP
seems like this si the correct structure like dan, can't the documentation be better xd so confusing
@Tiphiid wasp alright so it seemslike for enxtj13.2+ this is the correct scttureu
American black bear
if you don't mind, theme name ?
Tiphiid waspOP
now I dont get the error
now this works
Absolutelly hate nextauth docs
lol what was the issue?
Tiphiid waspOP
just the folder structure
if you scroll up you'll see how many tiems I tried this xd
maybe I jsut read it wrong...
but damn
@Tiphiid wasp maybe I jsut read it wrong...
American black bear
you probably read it wrong
@American black bear if you don't mind, theme name ?
Tiphiid waspOP
I think this is sublime text theme - but how do you check? 😄
Do you want my configs?
ohh ok, i can see that you didn't make it clear that you were using nextauth at the beginning
Tiphiid waspOP
But you know, funny thing is, I just woke up tried it again and solved it quickly xD
still sleep deprived a lil bit but
I think gotta sleep more xD
thanks for trying to help btw 🙂
we'll see if I can figure out the enxt part now xD
@American black bear if you don't mind, theme name ?
Tiphiid waspOP
does that help?
Tiphiid waspOP
Well no this is confuseing indeed https://next-auth.js.org/configuration/initialization#route-handlers-app

Still confused how I'mmeant to structure code xd

I got this:
import { authLogin } from "@/services/apis/xxx/requests/auth";
import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";

const handler = NextAuth({
  providers: [
    CredentialsProvider({
      name: "Credentials",

      credentials: {
        email: { label: "Email", placeholder: "Enter email"},
        password: { label: "Password", type: "password", placeholder: "Please enter password"},
      },
      async authorize(credentials, req) {
        
        // const res = await authLogin({
        //   email: "woo@mail.com",
        //   password: "ksdfsfewyb968h"
        // })
        // const res = await fetch("url/api/auth/login/", {
        //   method: "POST",
        //   headers: {
        //     "Content-Type": "application/json",
        //   },
        //   body: JSON.stringify({
        //     email: "test@mail.com",
        //     password: "khWLrThnCyb968h"
        //   }),
        // });
        // console.log(res)

        // const user = await res.json();
        if (user) {
          return user;
        } else {
          return null;
        }
      },
    }),
  ],

  pages: {
    signIn: "/auth/signIn",
  },
});

export { handler as GET, handler as POST}


Or am I doing anything wrong here?
The back-end gives me this when I use my functoin
{
    "access": "eyJhbGciOi2323242349.eyJ0b2tlbl90eX234234wiZXhwIjoxNzEyNzUzOTc5LCJpYXQiOjE2OTcyMDE5NzksImp0aSI6ImZhOTlkMmNiNWU4YjRhMjliZTZlMDQ0ZDdfwerwMTYyIiwidXNlcl9pZCI6MTR9.r8lAcUrkKP5oLwOEl--V7uOg48DMQ3ECtwrnSsE831w",
    "refresh": "",
    "user": {
        "pk": 14,
        "email": "test@gmail.com"
    }
}
but when I press login with the nextauth i get this:
{
    "error": null,
    "status": 200,
    "ok": true,
    "url": "https://domain.com/api/auth/signin?csrf=true"
}
The access is used as a refresh token and access token - I know bad back,endbut i didn't do it
Tiphiid waspOP
So you can't relallly do the about on client side
above*
I'm not sure exactly what you're trying to do. If you just need to protect the dashboard route you should be able to do that in your middleware.ts with just this

export { default } from "next-auth/middleware";

export const config = {
  matcher: ["/dashboard/*"],
  // matcher: ["/((?!register|api|login).*)"],
};
@Tiphiid wasp doesn't middleware run on the server though?
Yes, that's where auth should happen
@Marchy Yes, that's where auth should happen
Tiphiid waspOP
Right, that's the issue, since this is a full SPA I'm doing
So instead I just created this:
'use client'

import { usePathname, useRouter } from 'next/navigation'

import '../../src/styles/styles.scss'
import Provider from '../utils/provider'
import helperAuth from '@/utils/helperAuth'

import Login from './auth/login/page'
import Register from './auth/register/page'

export const ClientSideAuthCheck = ({ children }: any) => {
  const router = useRouter()
  const pathname = usePathname()
  const isAuth = helperAuth.checkIfAuthenticated()

  const object:any = {
    "/auth/login": <Login />,
    "/auth/register": <Register />
  }
  const activePathname = object[pathname]

  if (!isAuth) {
    if(activePathname) {
      if (!location.pathname.startsWith('/auth')) {
        router.replace(pathname);
      }
      return activePathname
    } else {
      router.replace("/auth/login")
      return object["/auth/login"]
    }
  }
  return children
}

export default function RootLayout({ children, }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <Provider>
          <ClientSideAuthCheck children={children} />
        </Provider>
      </body>
    </html>
  )
}
Not idea but...
does the work
the back-end is what it is and I can't change it
which is frustrating but oh well 🤷

I guess that part will have to be done poorly
Next.js is a fullstack framework. Of course you can do the auth check on server side still
@KINXZ Next.js is a fullstack framework. Of course you can do the auth check on server side still
Tiphiid waspOP
Yes,but not with that plugin right
I was thinking there was a neat way to do it
@Tiphiid wasp I was thinking there was a neat way to do it
what does helperAuth.checkIfAuthenticated() do?
@KINXZ what does helperAuth.checkIfAuthenticated() do?
Tiphiid waspOP
checks if token exists in localstorage
but even my solutoin is poor, because if you are logged in, you can still visit the login page, so that there isn't even finished xd
does nexthauth really not have a localstorage option xd for 'use client' xd
Komondor
You can get the session data on the client using next auth. I'm not sure what you mean by localstorage option
I just implemented this recently
Komondor
If that's what you're trying to do let me know and I can share examples
@Komondor If that's what you're trying to do let me know and I can share examples
Tiphiid waspOP
So the token is stores in localstorage, as accessToken, it doens't have refresh token, its just access token and that's it

You sign in via a specific API endpoint which seems like next auth doesn't like that xd
@Tiphiid wasp So the token is stores in localstorage, as accessToken, it doens't have refresh token, its just access token and that's it You sign in via a specific API endpoint which seems like next auth doesn't like that xd
Komondor
Let me get this straight. The client calls a signIn API endpoint with credentials (user+password), the API endpoint authenticates the credentials and then returns an accessToken? That accessToken is stored in local storage, and is used in future API requests?
@Komondor Let me get this straight. The client calls a signIn API endpoint with credentials (user+password), the API endpoint authenticates the credentials and then returns an accessToken? That accessToken is stored in local storage, and is used in future API requests?
Tiphiid waspOP
Yeah.

User presses login, an API endpoints is being sent with (user+password), the API endpoint authenticated by sneding back the accessToken, that then is stored in localstorage, and then is attached to every request.

Its bad but that's what it is : p
Komondor
That's not far off from what Next Auth does by default. Is there a reason you aren't using NextAuth's default functionality? NextAuth will check for the Bearer token for you.
coz they handle the auth really nice
with the routes etc isntead of what i got
mind sharing an example please? or the docs I should read for that
Komondor
Are you available now? I can screenshare
Let me know if I can dm you. I think the rules say not to but if you're ok with it I will
Komondor
we'll share the solution here once we figure it out