4

I´m using JWT authentication inside my ReactJS RelayJS network environment. All the token retrieval and processing in server and client are fine. I´m using react router v4 for routing.

My problem is when I receive a Unauthorized message from server (status code 401). This happens if the user points to an application page after the token has expired, ie. What I need to do is to redirect to login page. This is the code I wish I could have:

import { Environment, Network, RecordSource, Store } from 'relay-runtime';

const SERVER = 'http://localhost:3000/graphql';

const source = new RecordSource();
const store = new Store(source);

function fetchQuery(operation, variables, cacheConfig, uploadables) {
  const token = localStorage.getItem('jwtToken');

  return fetch(SERVER, {
    method: 'POST',
    headers: {
      Authorization: 'Bearer ' + token,
      Accept: 'application/json',
      'Content-type': 'application/json'
    },
    body: JSON.stringify({
      query: operation.text, // GraphQL text from input
      variables
    })
  })
    .then(response => {
      // If not authorized, then move to default route
      if (response.status === 401)
          this.props.history.push('/login') <<=== THIS IS NOT POSSIBLE AS THERE IS NO this.history.push CONTEXT AT THIS POINT
      else return response.json();
    })
    .catch(error => {
      throw new Error(
        '(environment): Error while fetching server data. Error: ' + error
      );
    });
}

const network = Network.create(fetchQuery);

const handlerProvider = null;

const environment = new Environment({
  handlerProvider, // Can omit.
  network,
  store
});

export default environment;

Naturally calling this.props.history.push is not possible as the network environment is not a ReactJS component and therefore has no properties associated.

I´ve tried to throw an error at this point, like:

      if (response.status === 401)
          throw new Error('Unauthorized');

but I saw the error on the browser console, and this cannot be treated properly in the code.

All I wanna do is to redirect to login page in case of 401 error received, but I can´t find a proper way of doing it.

Mendes
  • 17,489
  • 35
  • 150
  • 263

2 Answers2

0

I am not using relay but a render prop. I experienced kind of the same issue. I was able to solve it using the window object.

 if (response.statusText === "Unauthorized") {
     window.location = `${window.location.protocol}//${window.location.host}/login`;
 } else {
   return response.json();
 }
Adam Recvlohe
  • 334
  • 1
  • 11
0

You can go with useEnvironment custom hook.

export const useEnvironment = () => {
  const history = useHistory(); // <--- Any hook using context works here

  const fetchQuery = (operation, variables) => {
    return fetch(".../graphql", {...})
      .then(response => {
         //...
         // history.push('/login');
         //...
      })
      .catch(...);
  };

  return new Environment({
    network: Network.create(fetchQuery),
    store: new Store(new RecordSource())
  });
};

// ... later in the code

const environment = useEnvironment();

Or you can create HOC or render-prop component if you are using class-components.

btw: this way you can also avoid usage of the localStorage which is slowing down performance.

Melounek
  • 764
  • 4
  • 20