'How to use JWT stored in cookie in NEXT _middleware

This question extends my last question and is primarily for me who might get stuck again. I'm trying to rebuild an app in Next.js that has this login form using JWT tokens. On the previous app, I store the access token in memory using context API and the refresh token in an httpOnly cookie. However, I discovered in Next.js that you can use something called _middleware that runs on every request. I thought of just storing both in an httpOnly cookie then verify each request. I'm still testing it out for bugs but it runs well for the time being. Answer below.



Solution 1:[1]

In this code I'm using a library called jose instead of jsonwebtoken because Native Node.js APIs are not supported and you will get the following error . Using Jose comes from this stack overflow answer.

_Middlesware.tsx

import { NextResponse } from "next/server";
import { generateAccessToken, verifyRefreshToken } from "../_operations/jwt/jwt";
export default async function (
    req: {
        url?: any;
        cookies?: any;
    }
): Promise<NextResponse | void> {
    const { cookies } = req;
    const url: string = req.url;
    const refreshToken: string | undefined = cookies?.refresh_token_extreme;
    const accessToken: string | undefined = cookies?.access_token_extreme;
    const baseUrl: string = "http://localhost:3000";

    // vercel.svg is used in /login
    const unprotectedPaths: string[] = [
        `${baseUrl}/login`,
        `${baseUrl}/signup`,
        `${baseUrl}/favicon.ico`,
        `${baseUrl}/vercel.svg`,
        `${baseUrl}/_next/webpack-hmr`,
        `${baseUrl}/attachables/campus-images/image1.jpg`,
        `${baseUrl}/attachables/mnhs-images/logos/login_logo.png`,
        `${baseUrl}/attachables/mnhs-images/logos/mnhs_favicon_og.ico`,
    ];

    if (unprotectedPaths.includes(url)) return void 0;
    if (!refreshToken && url === `${baseUrl}/api/login`)
        return NextResponse.next();
    if (!accessToken && !refreshToken)
        return NextResponse.redirect(`${baseUrl}/login`);
    if (!accessToken && refreshToken) {
        const verifiedToken = await verifyRefreshToken(refreshToken)
        const newToken = await generateAccessToken(verifiedToken as any)
        return NextResponse.next().cookie('access_token_extreme', newToken, {
            secure: true,
            sameSite: "strict",
            httpOnly: true,
            path: "/",
            expires: new Date(Date.now() + 1000 * 60 * 10) // 10 minutes
        });
    }
    return NextResponse.next();
}

jwt.ts

For why do I have uprotectedPaths, refer to my last question

import * as jose from "jose";

export function generateAccessToken(user: {}): Promise<string> {
    return new Promise(async (resolve) => {
        const accessToken = await new jose.SignJWT({ user })
            .setProtectedHeader({ alg: "HS256" })
            .setExpirationTime("10m")
            .sign(new TextEncoder().encode(process.env.ACCESS_TOKEN_SECRET));
        return resolve(accessToken);
    });
}
export function generateRefreshToken(user: {}): Promise<string> {
    return new Promise(async (resolve) => {
        const refreshToken = await new jose.SignJWT({ user })
            .setProtectedHeader({ alg: "HS256" })
            .sign(new TextEncoder().encode(process.env.REFRESH_TOKEN_SECRET));
        return resolve(refreshToken);
    });
}
export function verifyRefreshToken(token: string) {
    return new Promise(async (resolve) => {
        const { payload: newToken } = await jose.jwtVerify(
            token,
            new TextEncoder().encode(process.env.REFRESH_TOKEN_SECRET)
        );
        return resolve(newToken);
    });
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Nugget