Communication between React Context and Apollo Client Link
Asked Answered
A

0

8

I am working on a single page webapp using React and Apollo Client, and I am wondering about how to correctly communicate authentication state between Apollo Client links and React context.

Within the client, I have written an AuthProvider context that supplies the current user information, so that anywhere in the component tree I can do

const authState = useAuthState()
const dispatch = useAuthDispatch()

and thus query and update the authentication information as I need. I have used this, for example, to write a PrivateRoute component that redirects the viewer if she is not authenticated:

const PrivateRoute: FunctionComponent<RouteProps> = ({ children, ...rest }) => {
    const authState = useAuthState()

    return (
        <Route
            {...rest}
            render={({ location }) =>
                authState.user ? (
                    children
                ) : (
                    <Redirect
                        to={{
                            pathname: "/login",
                            state: { from: location }
                        }}
                    />
                )
            }
        />
    )
}

This is all fine. My issue arises when combining this with my chosen form of authentication, which is JWT. I am storing the access token in the authState and the refresh token is set as an httpOnly cookie by the back-end on login.

But I have to send the access token as an Authorization: Bearer header on each request, which I want to do using an Apollo Link, as follows:

const authLink = setContext(async (_, { headers }) => {
    const token = getTokenFromAuthStateSomehow()

    return {
        headers: {
            ...headers,
            authorization: token ? `Bearer ${token}` : ""
        }
    }
})

But this is within an Apollo Link, where I of course don't have direct access to the React Context. getTokenFromAuthStateSomehow() is a function I do not know how to write.

Then the next issue is what happens when this request fails because the access token has expired. I want to use Apollo's onError to catch a 401 error from the API and retry the request by getting a refreshed token:

const retryLink = onError(({ networkError }) => {
    if (networkError) {
        const newToken = getRefreshedToken()

        if (newToken) {
            retryRequest()
            setTokenInAuthStateSomehow(newToken)
        }
    }
})

But then we have the same problem - now I need to send the new token back to authState, i.e. to React Context: setTokenInAuthStateSomehow() is a function I do not know how to write either.

So, the overarching question is: how do I communicate between an Apollo Link and React Context? Do I have to setup some listeners or events somehow? I would love any information or push in the right direction.

Almeta answered 17/11, 2020 at 12:16 Comment(2)
have you found. the solution? I am at exact same spot right now and can't figure it out :( all the examples out there store the JWT in the cookie or in the local storage which is not a problem to access from the apollo client but not suitable from the security perspective.Coronary
see this question #59232618 it has an answer that worked for me although it feels like a hack to me.Coronary

© 2022 - 2024 — McMap. All rights reserved.