Authorization working
This commit is contained in:
112
src/providers/AuthProvider.tsx
Normal file
112
src/providers/AuthProvider.tsx
Normal file
@@ -0,0 +1,112 @@
|
||||
import { api } from "@/util/AxiosUtil";
|
||||
import { createContext, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useState } from "react";
|
||||
import { Navigate, Outlet } from "react-router";
|
||||
|
||||
|
||||
type AuthProviderProps = {
|
||||
children: React.ReactNode;
|
||||
jwtStorageKey?: string;
|
||||
refreshTokenStorageKey?: string;
|
||||
}
|
||||
|
||||
type AuthProviderState = {
|
||||
jwt: string | null;
|
||||
setJwt: (token: string | null) => void;
|
||||
expiration: Date | null;
|
||||
setExpiration: (expiration: Date | null) => void;
|
||||
}
|
||||
|
||||
const initialState: AuthProviderState = {
|
||||
jwt: null,
|
||||
setJwt: () => null,
|
||||
expiration: null,
|
||||
setExpiration: () => null
|
||||
}
|
||||
|
||||
const AuthContext = createContext<AuthProviderState>(initialState);
|
||||
|
||||
|
||||
export function AuthProvider({
|
||||
children
|
||||
}: AuthProviderProps){
|
||||
const [ jwt, setJwt ] = useState<string | null>(null);
|
||||
const [ expiration, setExpiration ] = useState<Date | null>(null)
|
||||
|
||||
|
||||
const fetchToken = useCallback(async () => {
|
||||
try{
|
||||
const response = await api.get("/auth/refresh");
|
||||
//If the token is retrieved
|
||||
if((response.status === 200) && (!response.data.errors)){
|
||||
setJwt(response.data.token);
|
||||
setExpiration(new Date(atob(response.data.token.split(".")[1])));
|
||||
}
|
||||
//If the token cannot be retrieved
|
||||
else{
|
||||
setJwt(null);
|
||||
setExpiration(null);
|
||||
}
|
||||
}
|
||||
//If the token cannot be retrieved
|
||||
catch{
|
||||
setJwt(null);
|
||||
setExpiration(null);
|
||||
}
|
||||
}, [ setJwt, setExpiration ]);
|
||||
|
||||
|
||||
//Add the token to all queries
|
||||
useLayoutEffect(() => {
|
||||
if((expiration) && (expiration < new Date())){
|
||||
fetchToken();
|
||||
}
|
||||
|
||||
const authInterceptor = api.interceptors.request.use(config => {
|
||||
config.headers.Authorization = jwt ? `Bearer ${jwt}` : config.headers.Authorization;
|
||||
return config;
|
||||
});
|
||||
|
||||
return () => { api.interceptors.request.eject(authInterceptor); };
|
||||
}, [ jwt, expiration, fetchToken ]);
|
||||
|
||||
//Try to get the token on page load
|
||||
useEffect(() => {
|
||||
fetchToken();
|
||||
}, [ fetchToken ]);
|
||||
|
||||
|
||||
const currentTokens = useMemo(() => ({
|
||||
jwt,
|
||||
setJwt,
|
||||
expiration,
|
||||
setExpiration
|
||||
}), [ jwt, setJwt, expiration, setExpiration ]);
|
||||
|
||||
|
||||
return (
|
||||
<AuthContext.Provider value={currentTokens}>
|
||||
{children}
|
||||
</AuthContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function ProtectedRoute(){
|
||||
const { jwt } = useAuth();
|
||||
|
||||
if(!jwt){
|
||||
return <Navigate to="/login"/>;
|
||||
}
|
||||
|
||||
return <Outlet/>;
|
||||
}
|
||||
|
||||
|
||||
export const useAuth = () => {
|
||||
const context = useContext(AuthContext);
|
||||
|
||||
if(context === undefined){
|
||||
throw new Error("useAuth must be used within an AuthProvider");
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
Reference in New Issue
Block a user