1

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;

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
A.com
  • 1,466
  • 4
  • 19
  • 31
  • is there an easy way to test the token expiring besides waiting an hour for it to expire? Changing the time on the client didn't do anything which makes sense and i'm using a service to host my server so i don't know how to change the time on that. Anything else i'm missing? – Brandon M Aug 18 '20 at 02:47
  • @BrandonM I try to use accountsJS now... I avoid auth0 and firebase at all costs – A.com May 17 '21 at 15:58

1 Answers1

1

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.

A.com
  • 1,466
  • 4
  • 19
  • 31
  • I've run into a subsequent issue here where it refreshes up until the refresh token has expired. At that point, the server error just keeps coming back and my UI errors out... not sure how to handle this. – A.com Jul 25 '19 at 14:54