Last active
May 30, 2024 12:18
-
-
Save epicbytes/7628f9a341fc31985475157c2b285846 to your computer and use it in GitHub Desktop.
NextJS Authorization Files
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*** 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}`); | |
} | |
}, |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* /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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* /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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* /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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* /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