We are switching our authentication service from a custom implementation with cookies to an approach with refresh tokens by Auth0. The Auth0 react package provides us with a getAccessTokenSilently function which we use in an Interceptor context provider and add the token to our Axios client via an interceptor.
The structure of the different parts looks like this:
App.js
const App = () => {
return (
<Auth0Provider
domain={process.env.REACT_APP_AUTH0_DOMAIN}
clientId={process.env.REACT_APP_AUTH0_CLIENT_ID}
redirectUri={window.location.origin}
audience={process.env.REACT_APP_AUTH0_AUDIENCE}
>
<BrowserRouter>
<NotificationProvider>
<Interceptor>
<NotificationList />
<Router />
</Interceptor>
</NotificationProvider>
</BrowserRouter>
</Auth0Provider>
);
};
export default App;
apiClient.js
import axios from 'axios';
const apiClient = axios.create({
baseURL: process.env.REACT_APP_API,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
timeout: 10000,
});
export default apiClient;
Interceptor.js
const Interceptor = ({ children }) => {
const { addNotification } = useContext(NotificationContext);
const { getAccessTokenSilently } = useAuth0();
const [accessToken, setAccessToken] = useState();
useEffect(() => {
const getAccessToken = async () => {
try {
const token = await getAccessTokenSilently();
console.log('TOKEN: ', token);
setAccessToken(token);
} catch (error) {
console.log(error);
}
}
getAccessToken();
}, [getAccessTokenSilently]);
useMemo(() => {
apiClient.interceptors.request.use(
(config) => {
config.headers.Authorization = `Bearer ${accessToken}`;
return config;
},
(error) => {
return Promise.reject(error);
},
);
apiClient.interceptors.response.use(
(response) => {
return response;
},
(error) => {
if (error.message === 'Network Error') {
if (error?.response?.status === 504) {
addNotification('error', 'Oops!', 'gateway_timeout');
} else {
addNotification('error', 'Oops!', 'server_down');
}
} else {
addNotification('error', error.response?.data?.title || 'Oops!', error.response?.data?.description);
}
return Promise.reject(error);
},
);
}, [accessToken]);
return <>{children}</>;
};
export default Interceptor;
profileService.js
import apiClient from '../utils/apiClient';
export default {
getProfileInfo() {
return apiClient.get(`/profile`);
},
updateProfile(data) {
return apiClient.put('/profile', data);
},
};
We use different service files to separate our calls to the backend by topic and centralize them away from our user interface.
We are expecting that our access token would be added as Bearer token to our calls to the API but this is not the case. If we check the headers from our call we get Authorization: Bearer undefined while if we console.log the access token in the Interceptor provider we get an access token.
How can we set the token correctly via an interceptor?
EDIT 1: Extra code to show where the profileService functions are being used
Router.js
This is a part of the router.js file. in App.js is executing this code
const ProtectedRoutes = () => {
const { isAuthenticated, loginWithRedirect } = useAuth0();
return isAuthenticated ? <Outlet /> : loginWithRedirect();
};
const Router = () => {
const { isLoading } = useAuth0();
if (isLoading) {
return <Spinner />;
}
return (
<Routes>
<Route element={<GuestRoute />}>
<Route element={<GuestLayout />}>
<Route path='/account/verify' element={<VerifyAccount />} />
</Route>
</Route>
<Route element={<ProtectedRoutes />}>
<Route element={<OnboardingLayout />}>
<Route path='/onboarding' element={<Onboarding />} />
</Route>
<Route element={<AppLayout />}>
<Route path='/' element={<Navigate to='/profile' replace />} />
<Route path='/profile' element={<Profile />} />
</Route>
</Route>
<Route path='404' element={<NotFound />} />
<Route path='*' element={<Navigate to='/404' replace />} />
</Routes>
);
};
Profile page
Part of the profile page where the call to profileService.getProfileInfo is being called.
const ProfileOverview = () => {
useEffect(() => {
getProfileInfo();
}, []);
const getProfileInfo = async () => {
const { data } = await profileService.getProfileInfo();
reset({ ...data });
};
const {
handleSubmit,
register,
watch,
reset,
getValues,
formState: { errors, isDirty },
} = useForm({
resolver: useYupResolver(editProfileValidationSchema),
});
from Bearer token is not set in call to backend via Axios interceptor
No comments:
Post a Comment