5

I am new to Next.js and am storing a JWT authorization token in the client-side React Context and would like to 'pass' that token from the client-side context to a server component so that it can be retrieved from the server component via the headers() or cookies() functions.

I am assuming I need to 'set' these headers and cookies on the client-only code, but how?

I'd prefer to use headers() as I can control when/whether the credentials are sent or not; with cookies, I am assuming it's always sent with every request.

There is an Authentication documentation for the stable version of Next.js 13, but I have started with the app directory (beta) version which doesn't use getServerSideProps().

The stable documentation also mentions iron-session and I found this issue with an example of how to read the cookie in Next.js 13, but it doesn't show how to set it.

tl;dr How do I set headers and cookies client-side so they can be read via the headers() or cookies() functions in a server component?

Yilmaz
  • 35,338
  • 10
  • 157
  • 202
dayuloli
  • 16,205
  • 16
  • 71
  • 126

3 Answers3

2
  • When user signs in, you send the credentials to pages/api/auth/login. you check the verification and create a token.

I use cookies-next library to deal with the cookies.

import { setCookie } from "cookies-next";

export default async function handler(req: NextApiRequest,res: NextApiResponse)
 {
  // pass all the checking
  const token = await jwt.sign(credentials,secret)
  setCookie("jwt", token, { req, res, maxAge: 60 * 60 * 24 });
}
  • everytime you make a request, you attach the token to the header.

Using cookies-next

  import { getCookie } from "cookies-next";

  const jwt = getCookie("jwt");

 const response = await axios.get(`${process.env.BASE/whatever}`, {
    headers: {
      Authorization: `Bearer ${jwt}`,
    },
  });
  • Before in serverside functions, we could access to req object and check if the user is authenticated or not. To handle this in next.js I create an endpoint api/auth/me. I sent a request to this endpoint to check if the user has a valid jwt or not. I have to do this in top-level component of the app directory. I created an Authentication Context and inside useEffect I check the status of the user. I will keep track of the state:

in Authentication Context, client component

const fetchUser = async () => {
    setAuthState({
      data: null,
      error: null,
      loading: true,
    });
    try {
      const jwt = getCookie("jwt");

      if (!jwt) {
        return setAuthState({
          data: null,
          error: null,
          loading: false,
        });
      }

      const response = await axios.get(`${process.env.BASE/api/auth/me}`, {
        headers: {
          Authorization: `Bearer ${jwt}`,
        },
      });

      setAuthState({
        data: response.data,
        error: null,
        loading: false,
      });
    } catch (error: any) {
      setAuthState({
        data: null,
        error: error.response.data.errorMessage,
        loading: false,
      });
    }
  };

   useEffect(() => {
     fetchUser();
   }, []);
  • this is pages/api/auth/me endpoint.

I make a request here inside auth context

 export default async function handler(req: NextApiRequest,res: NextApiResponse)
 {
  const bearerToken = req.headers["authorization"] as string;
  const token = bearerToken.split(" ")[1];

  const payload = jwt.decode(token) as { email: string };

  if (!payload.email) {
    return res.status(401).json({
      errorMessage: "Unauthorized request",
    });
  }

  const user = yourFetchUser(payload.email)

  if (!user) {
    return res.status(401).json({
      errorMessage: "User not found",
    });
  }

  return res.json({...user});
}
  • check if the user is authenticated inside navbar.

this is a client component

const { data, loading } = useContext(AuthenticationContext);

Based on data you set conditionally display user name or sign in button.

  • finally using cookies-next sign out is very easy

    import { removeCookies } from "cookies-next";
    
    removeCookies("jwt");
    
Yilmaz
  • 35,338
  • 10
  • 157
  • 202
  • it is not safe to store access token in cookie as it gets exposted. – DragonKnight May 01 '23 at 03:34
  • @DK - it's encrypted and the jwt token is always stored in a cookies. It's as secure as it can be, but you can (and should) set an expiration on it though. – lentyai May 10 '23 at 00:55
0

I have not tried this myself, but I recently noticed this new section in the documentation about middlewares that makes it seem like it's possible to pass information to the server via request headers and not via cookies:

import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
 
export function middleware(request: NextRequest) {
  // Clone the request headers and set a new header `x-hello-from-middleware1`
  const requestHeaders = new Headers(request.headers);
  requestHeaders.set('x-hello-from-middleware1', 'hello');
 
  // You can also set request headers in NextResponse.rewrite
  const response = NextResponse.next({
    request: {
      // New request headers
      headers: requestHeaders,
    },
  });
 
  // Set a new response header `x-hello-from-middleware2`
  response.headers.set('x-hello-from-middleware2', 'hello');
  return response;
}
dayuloli
  • 16,205
  • 16
  • 71
  • 126
0

What @dayuloli said does work. I had used it.

middleware.ts

import { NextResponse } from 'next/server'

export async function middleware(request: NextRequest) {

    const headers = new Headers(request.headers);
    headers.set('middlewareSet', 'mydata');

    const resp = NextResponse.next({
      request: {
        headers
      }
    });    

    return resp;
}

server page:

export default async function PageServer() {
  
  const headersList = headers()
  const middlewareSet = headersList.get('middlewareSet') || ''
  
}
hyyou2010
  • 791
  • 11
  • 22