javascriptfirebasefirebase-authenticationreact-apolloapollo-client

refreshing a token with apollo-client + firebase auth


I'm having some trouble figuring out token refreshes when using apollo-client and firebase's auth service.

I've setup token refresh using apollo client before. Typically I use the apollo-link-error package (apollo-link-error docs).

My understanding is you get the latest token from firebase auth with a promise like:

firebase.auth().currentUser.getIdToken

How to use the Firebase refreshToken to reauthenticate?

reading the answer from this post:

When you make call from a browser .getIdToken(true) will automatically refresh your token.

based on my above reading, I came up with the below snippet

import { onError } from 'apollo-link-error';
import { Observable } from 'apollo-link'; // <-- Add Observable
import firebase from 'lib/firebase';

const errorLink = onError(
  ({ graphQLErrors, networkError, operation, forward }) => {

    let accessToken = window.localStorage.getItem('access_token');

    if (graphQLErrors) {
      // User access token has expired
      if (graphQLErrors[0].message.includes('Signature has expired')) {

        if (accessToken && accessToken !== 'undefined') {

          // Let's refresh token through async request
          return new Observable(observer => {
            firebase
              .auth()
              .currentUser.getIdToken(true)
              .then(function(idToken) {
                if (!idToken) {
                  window.localStorage.removeItem('access_token');
                  return console.log('Refresh token has expired');
                }

                window.localStorage.setItem('access_token', idToken);

                // reset the headers
                operation.setContext(({ headers = {} }) => ({
                  headers: {
                    // Re-add old headers
                    ...headers,
                    // Switch out old access token for new one
                    'login-token': idToken || null
                  }
                }));
                const subscriber = {
                  next: observer.next.bind(observer),
                  error: observer.error.bind(observer),
                  complete: observer.complete.bind(observer)
                };

                // Retry last failed request
                forward(operation).subscribe(subscriber);
              })
              .catch(error => {
                // No refresh or client token available, we force user to login
                observer.error(error);
              });
          });
        }
      }
    }
  }
);

export default errorLink;


Solution

  • The code above actually works!

    My issue was here:

    if (graphQLErrors[0].message.includes('Signature has expired')) {

    My server's error message is actually 'Token has expired'. I changed my if statement above, and all is well!

    I didn't find a lot of examples of apollo-client+firebase-auth online, so hopefully this breadcrumb can help others when working with apollo-client and firebase auth.