Skip to content

Instantly share code, notes, and snippets.

@moust
Created September 25, 2025 08:35
Show Gist options
  • Save moust/05a8b7e1397a7556e94fab6589be711c to your computer and use it in GitHub Desktop.
Save moust/05a8b7e1397a7556e94fab6589be711c to your computer and use it in GitHub Desktop.
import { env } from "@/lib/env";
import axios from "axios";
const api = axios.create({
baseURL: env.BASE_URL,
});
export default api;
import type { AxiosInstance, InternalAxiosRequestConfig } from "axios";
import {
createContext,
useContext,
useLayoutEffect,
useState,
type Dispatch,
type PropsWithChildren,
type SetStateAction,
} from "react";
import { redirect } from "react-router";
import api from "@/api";
type Token = string | null | undefined;
interface AuthContextType {
accessToken: Token;
setAccessToken: Dispatch<SetStateAction<Token>>;
}
const AuthContext = createContext<AuthContextType | undefined>(undefined);
export const useAuth = () => {
const authContext = useContext(AuthContext);
if (!authContext) {
throw new Error("useAuth must be used within a AuthProvider");
}
return authContext;
};
const AuthProvider = ({
accessToken: defaultAccessToken,
children,
}: PropsWithChildren<{ accessToken: Token }>) => {
const [accessToken, setAccessToken] = useState<Token>(defaultAccessToken);
useLayoutEffect(() => {
const requestInterceptor = initAuthorizationInterceptors(api, accessToken);
return () => {
api.interceptors.request.eject(requestInterceptor);
};
}, [accessToken]);
useLayoutEffect(() => {
const refreshInterceptor = initRefreshInterceptors(api, setAccessToken);
return () => {
api.interceptors.response.eject(refreshInterceptor);
};
}, []);
return (
<AuthContext.Provider value={{ accessToken, setAccessToken }}>
{children}
</AuthContext.Provider>
);
};
export function initAuthorizationInterceptors(
api: AxiosInstance,
accessToken?: string | null
) {
return api.interceptors.request.use(
(config: InternalAxiosRequestConfig<any> & { _retry?: boolean }) => {
config.headers.Authorization =
!config._retry && !config.headers.Authorization && accessToken
? `Bearer ${accessToken}`
: config.headers.Authorization;
return config;
}
);
}
export function initRefreshInterceptors(
api: AxiosInstance,
setAccessToken: (accessToken?: string) => void
) {
return api.interceptors.response.use(
(response) => response,
async (error) => {
const originalRequest = error.config;
if (
error.response.status === 403 &&
error.response.data.message === "Unauthorized"
) {
try {
// Appeler l'endpoint de refresh qui utilise les cookies
const response = await api.post("/api/refreshToken");
setAccessToken(response.data.accessToken);
originalRequest.headers.Authorization = `Bearer ${response.data.accessToken}`;
originalRequest._retry = true;
return api(originalRequest);
} catch {
setAccessToken(undefined);
// Rediriger vers la page de connexion
throw redirect("/login");
}
}
return Promise.reject(error);
}
);
}
export default AuthProvider;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment