Created
September 25, 2025 08:35
-
-
Save moust/05a8b7e1397a7556e94fab6589be711c to your computer and use it in GitHub Desktop.
This file contains hidden or 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 { env } from "@/lib/env"; | |
import axios from "axios"; | |
const api = axios.create({ | |
baseURL: env.BASE_URL, | |
}); | |
export default api; |
This file contains hidden or 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 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