Hey guys! A little question for those who use Next...
# javascript
m
Hey guys! A little question for those who use NextJS or NuxtJS. How to you guys handle authentication? Client side, or on the SSR side of those frameworks?
e
Have you seen the examples? I Auth on both server side and client side depending on what the page does
m
Yes, i've studied them, but i'm not sure i understood each use case :D. Basically, i'm used with the clasic way of doing things, node js server + frontend framework and i always think i'm supposed to do it client side, and i don't really know what is the case for ssr
j
perhaps we should offer more distinct examples for SSR and client side. we currently try to combine both use cases into the same example
f
I've been running an implementation that I found from this article: https://dev.to/dabit3/magic-link-authentication-and-route-controls-with-supabase-and-next-js-leo
basically upon login, the auth api route is called, which sets an httpOnly cookie. This cookie can then be read during SSR calls such as getServerSideProps. If that token is no longer valid, I redirect them to the login
c
âž• , I'm also using a cookie and validating. Depends on what you want to do, I just do a redirect directly from the server.
Copy code
const { user } = await supabase.auth.api.getUserByCookie(req);

  const isAuthenticated = user ? user.role === 'authenticated' : false;

  if (isAuthenticated) {
    return { props: {} };
  }
  return {
    props: {},
    redirect: { destination: '/login', permanent: false },
  };
f
I wish I could use that
getUserByCookie
method, but I need the token for backend authentication. If anyone's curious, here's kind of what my implementation is looking like:
Copy code
export const getServerSideProps: GetServerSideProps = async (
  ctx
): Promise<any> => {
  const token = cookies(ctx)['sb:token'];

  if (!token) return redirectToLogin({ reason: 'AccessDenied', server: true });

  const client = initializeApollo({ headers: ctx?.req?.headers });

  const context = {
    headers: {
      Authorization: `Bearer ${token}`,
    },
  };

  try {
    // query for profile page data
    await client.query({
      query: MeDocument,
      context,
    });

    return addApolloState(client, {
      props: {
        context,
      },
    });
  } catch (err) {
    console.error({ err });
    return { notFound: true };
  }
};
c
Ahh I see. Does your BE client then validate the token and get the user? I'm just wondering on how the authentication process gets verified. Since you could possibly have an expired token, an invalid token, etc. This code doesn't seem to me like it's protecting the page, it's actually just returning something or redirects you to a 404. My usual workflow is to 1. Protect the page with
getServerSideProps
2. Protect the routes by using row level security in my APIs So I guess the question is are you trying to protect pages or protect your data? In this code above it looks like you are just trying to protect your data and then if there is an error, it takes you to a 404.
f
Yea I've got the
notFound: true
return as just a placeholder at the moment. I'm going to be reworking it soon to actually act as more of a page protection
m
Thanks @frubalu ! I’ll check that link. Also, @claud9 do you have a repo i could check? Thanks a lot guys for the info
@ankurpauldev do you have any implementation? I would love to check a@project
c
@User , sorry nothing that is public but if you need any help I'm happy to post an example. If you follow that tutorial posted above, it's very similar to what I'm doing. 1. I protect each page, with the code I've shown above where it redirects you if you aren't authenticated. 2. For any protected page, I use row level security for the data. 3. If you want to protect your APIs too, you can pass the headers from the browsers and do something like this... where you do a double check for authentication and set the token for the client so it knows who has access (row level security)
Copy code
const { user } = await supabase.auth.api.getUserByCookie(req);
  const isAuthenticated = user ? user.role === 'authenticated' : false;
  const cookies = new Cookies(req, res);
  const token = cookies.get('sb:token');

  if (isAuthenticated && token) {
    supabase.auth.setAuth(token);
I like to handle everything server side with cookies. That way it's a lot better experience and everything is decoupled and has a single responsibility.
m
And where do you handle all of this? in what method. getServerSideProps or in api folder?
c
These are for my protected pages
Copy code
export async function getServerSideProps({
  req,
}: NextPageContext): Promise<GetServerSidePropsResult<Props | {}>> {
  const { user } = await supabase.auth.api.getUserByCookie(req);

  const isAuthenticated = user ? user.role === 'authenticated' : false;

  if (isAuthenticated) {
    return { props: {}};
  }

  return {
    props: {},
    redirect: { destination: '/login', permanent: false },
  };
}
APIs
Copy code
const handler = async (
  req: NextApiRequest,
  res: NextApiResponse
): Promise<void> => {
  const { user } = await supabase.auth.api.getUserByCookie(req);
  const isAuthenticated = user ? user.role === 'authenticated' : false;
  const cookies = new Cookies(req, res);
  const token = cookies.get('sb:token');

  if (isAuthenticated && token) {
    supabase.auth.setAuth(token);

    //DO something
    res.status(200).end();
  } else {
    res.status(401).end();
  }
};

export default handler;
And then the tutorial above, will show you how to set cookies but I did it this way, with an API endpoint.
Copy code
const handler = (req: NextApiRequest, res: NextApiResponse): void => {
  if (req.body.event) {
    supabase.auth.api.setAuthCookie(req, res);
    res.status(200).end();
  } else {
    res.status(400).end();
  }
};
m
Thaks a lot for the code. I'll try to implement what you've told me 😄