Skip to content

Instantly share code, notes, and snippets.

@culttm
Forked from epicbytes/example.api.ts
Created January 15, 2024 08:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save culttm/064c6c7690be606663e505f6b82ceeda to your computer and use it in GitHub Desktop.
Save culttm/064c6c7690be606663e505f6b82ceeda to your computer and use it in GitHub Desktop.
NextJS Authorization Files
/*** 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}`);
}
},
/* /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;
}
/* /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: JSON.parse(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;
/* /pages/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;
import { NextRequest, NextResponse } from "next/server";
import { isTokenValid, parseJwt } from "commons/src/jwt";
export const config = {
matcher: "/lk/:path*",
};
export async function middleware(req: NextRequest) {
const url = req.nextUrl.clone();
url.pathname = "/";
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);
}
/* /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);
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment