Skip to content

Instantly share code, notes, and snippets.

@adeleke5140
Last active November 8, 2023 13:30
Show Gist options
  • Save adeleke5140/2689033e42761b372467c54250af9d96 to your computer and use it in GitHub Desktop.
Save adeleke5140/2689033e42761b372467c54250af9d96 to your computer and use it in GitHub Desktop.
Access and Refresh Token feature with Axios in NextJS

Handling Refresh Token with NextJS API Route and Axios Interceptors

This is a short example on how to leverage nextJS API route to get and set cookies from an external server.

It gets the job done while not being the most efficient approach.

import axios, { CreateAxiosDefaults } from 'axios'
import { refreshToken as refreshTokenAdapter} from './session'
export const getJSONContentHeaders = () => {
"Content-Type": "application/json",
Accept: "application/json"
}
const axiosConfig: CreateAxiosDefaults = {
baseURL = '/url-to-backend'
headers: getJSONContentHeaders()
}
export default axios.create(axiosConfig)
export const axiosPrivate = axios.create({
...axiosConfig,
withCredentials: true,
})
export const getAuthHeader = (token: string) => ({
"Authorization": `Bearer ${token}`
})
export const setupAxiosInterceptors = (accessToken?: string, refreshToken: () => Promise<string> = refreshTokenAdapter) => {
const requestIntercept = axiosPrivate.interceptors.request.use(
async (config) => {
if (!config.headers["Authorization"]) {
let token = accessToken ?? await refreshToken();
config.headers["Authorization"] = getAuthHeader(token)['Authorization'];
}
return config;
},
(error) => Promise.reject(error)
);
const responseIntercept = axiosPrivate.interceptors.response.use(
(response) => response,
async (error) => {
const prevRequest = error?.config;
if (error?.response?.status === 403 && !prevRequest?.sent) {
prevRequest.sent = true;
const newAccessToken = await refreshToken();
prevRequest.headers["Authorization"] = getAuthHeader(newAccessToken)['Authorization'];
return axiosPrivate(prevRequest);
}
return Promise.reject(error);
}
);
return {
axios: axiosPrivate,
cleanupInterceptors: () => {
axiosPrivate.interceptors.request.eject(requestIntercept);
axiosPrivate.interceptors.response.eject(responseIntercept);
}
};
}
import axios from "axios";
const getErrorMessage = (err: unknown) => {
let message = "Something went wrong";
if (axios.isAxiosError(err)) {
message = err.response?.data.message;
} else if (err instanceof Error) {
return (message = err.message);
} else {
message = String(err);
}
return message;
};
export { getErrorMessage };
//api route for handling login /api/login
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from "next";
import { axiosPrivate } from "./axios";
import { isAxiosError } from "axios";
import { getErrorMessage } from "./getErrorMessage";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
//get whatever payload you are sending from the client
const formBody = req.formbody
try {
const response = await axiosPrivate.post(
"/sign-up",
formbody
);
if (response.status !== 200) throw new Error("Invalid response");
//if there is a cookie
//if there is none, remove this line
const cookies = response.headers?.["set-cookie"];
if (!cookies) {
throw Error("An error occurred try again later");
}
//you can analyze pick out the refresh from the response and set it to a cookie, there probabaly is a way of fine-tuning it but you get the drift.
res.setHeader("Set-Cookie", jwtCookie);
res.status(200).json(response.data);
} catch (err) {
const responseStatus = isAxiosError(err) && err.response ? err.response.status : 500;
const message = getErrorMessage(err);
console.error({
message,
responseStatus
})
res.status(responseStatus).json(message);
}
}
import axios from "./axios";
import { getErrorMessage } from "./getErrorMessage";
import { isAxiosError } from "axios";
import { NextApiRequest, NextApiResponse } from "next";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
try {
const response = await axios.get("/refresh", {
headers: {
cookie: req?.headers?.cookie as string,
},
});
if (response.status !== 200) throw new Error("Invalid response");
res.status(200).json(response.data);
} catch (err) {
const responseStatus = isAxiosError(err) && err.response ? err.response.status : 500;
const message = getErrorMessage(err);
console.error({
message,
responseStatus
})
res.status(responseStatus).json(message);
}
}
const REFRESH_ENDPOINT = '/refresh/endpoint'
//alternatively to fetch, just use the axios instance with axios.get
const refreshToken = async () => {
const res = await fetch(REFRESH_ENDPOINT, {
headers: {
"Content-Type": "application/json",
Accept: "application/json"
}
})
if(!res.ok) throw new Error('unauthorized)
const data = await res.json()
return data.token
}
export { refreshToken }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment