Authorization 🔐

The authorization flow is described here.

To authorize the user, the Yoli API expects an authorization header on almost every request. To realize this concept we used a link in the middleware which adds the header to each request if it exists.

This Link is part of the middleware described in Using GraphQL.

const appHeaderLink = new ApolloLink((operation, forward) => {
  // fetch the headers w/ authrization header
  const headers = requestInterceptor.getStateHeaders();

  // add the authorization to the headers
  operation.setContext({
    headers
  });

  return forward(operation);
});

Extended XMLHttpRequest

To automatically refetch the access token when a request responds with 401, we needed to overwrite the XMLHttpRequest to retry itself after fetching a new access token so that f.e. Angular resolvers dont reject.

// Hack to refire an unauthorised request after fetching
// a new refresh token
// Had to do it this way to satisfy angular resolvers
var orig_XMLHttpRequest = window.XMLHttpRequest;
var userService = this.userService;
var appState = this.appState;
window.XMLHttpRequest = function() {
  let xhr = new orig_XMLHttpRequest();
  xhr.onreadystatechange = (event) => {
    let crntReq = event.currentTarget;
    // readystate is 4 when the request is completed
    if (crntReq.status == 401 && crntReq.readyState == 4) {
      // reopen the request
      crntReq.open('POST', '/next/graphql/', true);
      let token = appState.refreshToken;
      appState.delete('requestHeaders');
      userService
        .getAccessToken(token)
        .subscribe((auth) => {
          // reset request headers
          xhr.requestHeaders.forEach(element => {
            const value = element.header === 'Authorization' 
              ? auth 
              : element.value;
            xhr.setRequestHeader(element.header, value);
          });
          // resend the request
          crntReq.send(xhr.payload);
        })
    }
  }

  // save payload
  let oldSend = xhr.send.bind(xhr);
  xhr.send = (payload) => {
    xhr.payload = payload;
    oldSend(payload);
  }

  // save request headers
  let oldSetRequestHeader = xhr.setRequestHeader.bind(xhr);
  xhr.requestHeaders = [];
  xhr.setRequestHeader = (header, value) => {
    xhr.requestHeaders.push({
      header,
      value
    });
    oldSetRequestHeader(header, value);
  }
  return xhr;