Skip to content

Instantly share code, notes, and snippets.

@epicbytes
Last active January 20, 2025 20:45
Show Gist options
  • Select an option

  • Save epicbytes/7628f9a341fc31985475157c2b285846 to your computer and use it in GitHub Desktop.

Select an option

Save epicbytes/7628f9a341fc31985475157c2b285846 to your computer and use it in GitHub Desktop.

Revisions

  1. epicbytes revised this gist Oct 14, 2022. 4 changed files with 88 additions and 7 deletions.
    22 changes: 22 additions & 0 deletions example.api.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,22 @@
    /*** function that used as middleware ***/

    accessToken: async (name) => {
    if (typeof document === "undefined") return "";
    let token = document.cookie
    .split(";")
    .filter((cookie) => cookie.startsWith("token"))[0];

    if (!token) {
    const response = await fetch("/api/refresh", { method: "POST" });
    const data = await response.json();
    if (!data.token) {
    Router.push("/logout");
    return Promise.reject();
    }
    return Promise.resolve(`Bearer ${data.token}`);
    } else {
    if (typeof token === "undefined") return "expired";
    const [_, accessToken] = token.split("=");
    return Promise.resolve(`Bearer ${accessToken}`);
    }
    },
    2 changes: 1 addition & 1 deletion logout.tsx
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    /* /pages/lk/logout.tsx */
    /* /pages/logout.tsx */
    import { GetServerSideProps } from "next";

    const LogoutPage = () => {
    41 changes: 35 additions & 6 deletions middleware.ts
    Original file line number Diff line number Diff line change
    @@ -1,14 +1,43 @@
    import { NextRequest, NextResponse } from "next/server";
    import { isTokenValid } from "commons/src/jwt";
    import { isTokenValid, parseJwt } from "commons/src/jwt";

    export const config = {
    matcher: "/lk/:path*",
    };

    export function middleware(req: NextRequest) {
    export async function middleware(req: NextRequest) {
    const url = req.nextUrl.clone();
    url.pathname = "/";
    return isTokenValid(req.cookies?.get("token"), "customer")
    ? NextResponse.next()
    : NextResponse.redirect(url);
    }
    const { cookies } = req;

    const nowUnix = (+new Date() / 1e3) | 0;
    const token = req.cookies?.get("token");
    const refresh_token = req.cookies?.get("refresh_token");

    const newResponse = NextResponse.next();

    let tokenIsValid = isTokenValid(token, "customer");

    if (!tokenIsValid && !!refresh_token) {
    const response = await fetch(`${process.env.BASE_URL}/user/refresh_token`, {
    body: JSON.stringify({
    refresh_token: refresh_token,
    }),
    headers: {
    "Content-Type": "application/json",
    },
    method: "POST",
    });
    const { access_token } = await response.json();
    const access_token_decoded: { exp: number } = parseJwt(access_token);

    newResponse.cookies.set("token", access_token, {
    path: "/",
    maxAge: access_token_decoded.exp - nowUnix,
    });

    tokenIsValid = true;
    }

    return tokenIsValid ? newResponse : NextResponse.redirect(url);
    }
    30 changes: 30 additions & 0 deletions refresh.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,30 @@
    /* /api/refresh.ts */

    import { NextApiRequest, NextApiResponse } from "next";
    import { CustomApi } from "@/services";
    import { parseJwt } from "commons";

    export default async (req: NextApiRequest, res: NextApiResponse) => {
    const { cookies } = req;
    const nowUnix = (+new Date() / 1e3) | 0;
    const CustomerApi = new CustomApi();
    try {
    const { access_token } =
    await CustomerApi.customerCustomRefreshTokenRequestWrapper({
    body: {
    refresh_token: cookies["refresh_token"],
    },
    });
    const access_token_decoded: { exp: number } = parseJwt(access_token);
    res.setHeader("Set-Cookie", [
    `token=${access_token}; Max-Age=${
    access_token_decoded.exp - nowUnix
    }; Path=/`,
    ]);
    res.status(200);
    res.send({ token: access_token });
    } catch (error) {
    // we don't want to send status 401 here.
    res.send(error);
    }
    };
  2. epicbytes revised this gist Sep 23, 2022. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion login.ts
    Original file line number Diff line number Diff line change
    @@ -10,7 +10,7 @@ const handlerLogin = async (req: NextApiRequest, res: NextApiResponse) => {
    try {
    const { access_token, refresh_token } =
    await CustomerApi.customerSignInRequestWrapper({
    body: req.body,
    body: JSON.parse(req.body),
    });
    const access_token_decoded: { exp: number } = parseJwt(access_token);
    const refresh_token_decoded: { exp: number } = parseJwt(refresh_token);
  3. epicbytes created this gist Sep 21, 2022.
    23 changes: 23 additions & 0 deletions jwt.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,23 @@
    /* /commons/src/jwt.ts */
    export type TokenGeneric = {
    exp: number;
    id: number;
    role: string;
    };

    export function parseJwt(token: string): TokenGeneric | null {
    try {
    return JSON.parse(atob(token.split(".")[1]));
    } catch (e) {
    return null;
    }
    }

    export function isTokenValid(token: string, role: string): boolean {
    if (!token) return false;
    const nowUnix = (+new Date() / 1e3) | 0;
    const decodedToken = parseJwt(token);
    if (decodedToken === null) return false;
    if (decodedToken.role !== role) return false;
    return decodedToken.exp > nowUnix;
    }
    34 changes: 34 additions & 0 deletions login.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,34 @@
    /* /pages/api/login.ts */
    import { NextApiRequest, NextApiResponse } from "next";
    import { CustomApi } from "@/services";
    import { parseJwt } from "commons/src/jwt";

    const handlerLogin = async (req: NextApiRequest, res: NextApiResponse) => {
    const nowUnix = (+new Date() / 1e3) | 0;
    const CustomerApi = new CustomApi();

    try {
    const { access_token, refresh_token } =
    await CustomerApi.customerSignInRequestWrapper({
    body: req.body,
    });
    const access_token_decoded: { exp: number } = parseJwt(access_token);
    const refresh_token_decoded: { exp: number } = parseJwt(refresh_token);

    res.setHeader("Set-Cookie", [
    `token=${access_token}; Max-Age=${
    access_token_decoded.exp - nowUnix
    }; Path=/`,
    `refresh_token=${refresh_token}; Max-Age=${
    refresh_token_decoded.exp - nowUnix
    }; Path=/; HttpOnly=true`,
    ]);

    res.send({ refresh_token });
    } catch (e) {
    res.status(401);
    res.send({ message: "error_while_login" });
    }
    };

    export default handlerLogin;
    19 changes: 19 additions & 0 deletions logout.tsx
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,19 @@
    /* /pages/lk/logout.tsx */
    import { GetServerSideProps } from "next";

    const LogoutPage = () => {
    return <></>;
    };

    export const getServerSideProps: GetServerSideProps = async (context) => {
    context.res.setHeader("Set-Cookie", [
    `token=deleted; Max-Age=0; Path=/`,
    `refresh_token=deleted; Max-Age=0; Path=/`,
    ]);
    return {
    redirect: { permanent: false, destination: "/" },
    props: { initialState: {} },
    };
    };

    export default LogoutPage;
    14 changes: 14 additions & 0 deletions middleware.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,14 @@
    import { NextRequest, NextResponse } from "next/server";
    import { isTokenValid } from "commons/src/jwt";

    export const config = {
    matcher: "/lk/:path*",
    };

    export function middleware(req: NextRequest) {
    const url = req.nextUrl.clone();
    url.pathname = "/";
    return isTokenValid(req.cookies?.get("token"), "customer")
    ? NextResponse.next()
    : NextResponse.redirect(url);
    }