hey I have an architectural question, I am using auth0 in combination with my own API. Setup made after the official documentation how to setup react sdk.
So far so good. Now I want to have an onboarding to ask a user for some more info which is being send to my own backend. So my idea was inside my app I wrap my routes with a user provider which fetches the userinfo from my backend. I also extended the HOC with another withOnboarding HOC which uses the useUser context to decide if user has onboarded or not.
index.tsx
ReactDOM.render(
<React.StrictMode>
<Auth0Provider {...providerConfig}>
<BrowserRouter>
<App />
</BrowserRouter>
</Auth0Provider>
</React.StrictMode>,
document.getElementById("root")
);
app.tsx
export default function App() {
const { isLoading, error, isAuthenticated } = useAuth0();
if (error) {
return <div>Oops... {error.message}</div>;
}
if (isLoading) {
return <Loading />;
}
return (
<Router>
<ThemeProvider theme={theme}>
<UserProvider>
<CssBaseline />
{isAuthenticated && <Navbar />}
<Switch>
<Route path="/login">
<LoginPage />
</Route>
<ProtectedRoute path="/" exact component={HomePage} />
<ProtectedRoute path="/profile" component={ProfilePage} />
</Switch>
</UserProvider>
</ThemeProvider>
</Router>
);
}
ProtectedRoute.tsx
export const ProtectedRoute = ({
component,
...args
}: React.PropsWithChildren<any>) => (
<Route
render={(props) => {
let Component = withAuthenticationRequired(withOnboarding(component), {});
return <Component {...props} />;
}}
{...args}
/>
);
withOnboarding.tsx
const withOnboarding = <P extends object>(
Component: ComponentType<P>
): FC<P> => {
return function WithOnboarding(props: P): JSX.Element {
const history = useHistory();
const isOnboarding = history.location.pathname === "/onboarding";
const { isLoading, isOnboarded } = useUser();
return isLoading ? (
<LinearProgress />
) : !isOnboarded && !isOnboarding ? (
<Redirect to="/onboarding" />
) : isOnboarded && isOnboarding ? (
<Redirect to="/" />
) : (
<Component {...props} />
);
};
};
userProvider.tsx
const UserProvider = (opts: UserProviderOptions): JSX.Element => {
const { getAccessTokenSilently } = useAuth0();
const { children } = opts;
const [state, dispatch] = useReducer(reducer, initialUserState);
useEffect(() => {
(async (): Promise<void> => {
const token = await getAccessTokenSilently();
await http
.authorized(token)
.get("/rest/v1/users/me")
.then((result) => {
console.log(result);
const user = result.data;
dispatch({ type: "INITIALISED", user: user });
})
.catch(function (error) {
dispatch({ type: "ERROR", error: loginError(error) });
});
})();
}, [getAccessTokenSilently]);
return (
<UserContext.Provider
value={{
...state,
registerUser,
}}
>
{children}
</UserContext.Provider>
);
};
All that usually works pretty well, but sometimes (especially when refreshing the page) I get an error about the token is missing or expired. My backend logs say “Token used before issued”! I was googleing it and found things like the time not being synced?!
I dont really know what to do, due to this issue is just occasionally happening… But I wanted to make sure, I handle auth0 and my backend in my project correctly… I was thinking that my user provider maybe tries to fetch the data too early, but when I log the token before the fetch its always present (but maybe wrong?!) On the other hand I kinda make sure in App.tsx that the userProvider will only load after auth0 has finished initialising and isLoading is false?! Anyone an idea?