0

I am using apollo server to set cookies in response headers after a user successfully logs in.

const { accessToken, refreshToken } = getNewTokens(user);
res.cookie("access-token", accessToken);
res.cookie("refresh-token", refreshToken);

On my client side I am using react and this is how I handle the login form submission

    const formik = useFormik({
        initialValues: {
            usernameOrEmail: "",
            password: "",
        },

        onSubmit: async ({ usernameOrEmail, password }) => {
            const {
                data: {
                    login: { error },
                },
            } = await login({
                variables: {
                    usernameOrEmail,
                    password,
                },
            });
            if (error) {
                setResErr(error);
            } else {
                setResErr(null);
                history.push("/group/1");
            }
        },

I have defined /group/1 as a private route and this is how routing is done

    <ApolloProvider client={client}>
        <Router>
            <Route path="/register" exact component={Register} />
            <Route path="/login" exact component={Login} />
            <PrivateRoute path="/profile/:userId" exact component={Profile} />
            <PrivateRoute path="/group/:groupId" exact component={Group} />
            <PrivateRoute path="/chat/:userId" exact component={Chat} />
        </Router>
    </ApolloProvider>,

The PrivateRoute component looks like this

const PrivateRoute = ({ component: Component, ...rest }) => {
    const user = getUserfromCookie();
    return (
        <Route
            {...rest}
            render={(props) =>
                user && user.userId ? (
                    <React.Fragment>
                        <Navbar />
                        <Component {...props} />
                    </React.Fragment>
                ) : (
                    <Redirect to="/login" />
                )
            }
        />
    );
};

This is the getCookies function

export const getCookies = () => {
    let accessToken, refreshToken;
    try {
        accessToken = Cookies.get("access-token");
        refreshToken = Cookies.get("refresh-token");
    } catch (err) {
        return { accessToken: null, refreshToken: null };
    }
    return { accessToken, refreshToken };
};

export const getUserfromCookie = () => {
    const { accessToken } = getCookies();
    let user = null;
    try {
        user = decode(accessToken);
    } catch (err) {
        console.log(err);
    }
    return user;
};

Now when I successfully submit the form I am hoping that the cookies will be set and I will be redirected. But when I see my console the cookies are set Console image

however, I am not being redirected to the group's page. But if I refresh the login page and enter the credentials again it works just as expected. I tried to call the getUserfromCookie after form submission ie

            const {
                data: {
                    login: { error },
                },
            } = await login({
                variables: {
                    usernameOrEmail,
                    password,
                },
            });
            console.log(getUserfromCookie());
            if (error) {
                setResErr(error);
            } else {
                setResErr(null);
                history.push("/group/1");
            }

and it does display the user. I also tried logging its output inside the PrivateRoute component but I see nothing on the console perhaps because it quickly reroutes to the login page. Am I doing something wrong here?

robin
  • 13
  • 6
  • The problem is, `PrivateRoute` will call the `getUserfromCookie` while it renders initially. Post logging in, you need to trigger this event again. Use a state and change it when you login, when state changes PrivateRoute will rerender causing the function to be called again – Prasanna Sep 21 '20 at 13:33
  • @Prasanna what exactly do you mean by `triggering this event again`. I mean won't this method be called itself when PrivateRoute is rerendered due to history.push(). – robin Sep 21 '20 at 13:55
  • i don't think so. you can put a console statement and verify the same. – Prasanna Sep 21 '20 at 14:02
  • Yes, I don't see anything in the console...But can you elaborate a bit – robin Sep 21 '20 at 14:03
  • `` will render when you load the page initially. `history.push()` will merely change the location (will not reload the page) and your react router will then render the mapped component. You can move the `user` to a state. If you are using, redux map the store key to the component, or you can pass the state to the PrivateRoute as a props, or useContext. Whenever a state changes the component re-renders making it possible to achieve what you are planning for. – Prasanna Sep 22 '20 at 14:04
  • I see, thanks alot for the response. I am not using redux but I managed to get around the issue by wrapping the routes inside a `Switch`. Also this helped https://stackoverflow.com/questions/43520498/react-router-private-routes-redirect-not-working – robin Sep 23 '20 at 04:28

0 Answers0