Firebase auth for graphql clients

This’ll be quite a long post, or maybe a series of them, as auth is always complicated. Lets first of all set the scene. This is a VueJs client that needs access, both authenticated and open, to a back end graphql apollo server.

Firebase client side auth

There are lots of approaches to auth with graphql. I’m using Firebase Auth (but we’ll need to use the underlying Google Identity platform too – but more of that later). So here’s the background.
  • There are multiple instances of the server as it’s running in a kubernetes scaling cluster, so it’s stateless – no session management or cookies.
  • The client app uses standard firebase auth and ends up with a uid for the currently logged on user.
  • This uid needs to be passed reliably and securely to the backend server so it can determine if the request is allowed.
  • Other clients, both server side and client side need to make authenticated and non authenticated requests to the server too. In my case these are cloud functions, cloud run containers, graphiql, Apps Script and various other node utilities
  • Various api keys are in use for tracking origin usage

Passing the uid safely to the server.

Both the apikey and the uid are passed in request headers. The simplest way to do this is to create apollolink which will add headers to every client request.
You’ll need to install and require setContext like this, as its not in the regular apollo package.

then create your link middleware..

which you can add to your other apollo links when you instantiate the Apollo client.

Generating a jwt token for the Firebase Uid

Of course you don’t want to send the uid in plain text, or in some way it can be tampered with so you can use firebase to generate a jwt token userIDtoken for you. Here activeUser is a firebase user object.

List of packages you’ll need

There are many variations of Apollo  bundle packages to choose from and this may not work for you depending on what other options you are using, but to save you some time, here’s the full list I use to enable all this.

And that’s it for the client side!

Handling auth server side

Firebase provides a way to untangle the jwt to get back to the original firebase uid, so we’ll just use some middleware to validate each request before it hits apollo. Ignore the stuff about proxy – we’ll come to that in a moment.

Express middleware

This middleware will get called to have a look at every request and will reject it if its an unknown/ bad apikey or the jwt token doesn’t check out. Note that you can use res.locals to store the results so it will be available further along the middleware chain.

This example api keeps a list of known api keys along with what they are expected to be able to do, so the apiKey received is checked against that list.

 

Extract the headers that should have been sent from a client.

Check the apikey is good

Wrapper

Once the apikey combination is verified in principle, it’s time to verify and unpack the jwt.

Verify the user ID token

This uses the firebase admin SDK

Accessing in the query context

Assuming that everything is in order, the middleware will pass through to the next stages. Eventually Graphql will handle the query, and the query context will need to be populated with a few things, including the auth information we’ve just validated.  The context function will include this in each resolver call. There’s a few extra things here, but for this example I’ve cut out all but authPack.providerUser which will contain either null (if nobody was logged on in the client, or a firebase user object)

And this is injected into the server options like this

Proxy

There will be times when the server needs to run something on behalf of a user who is not logged on at this moment. In my use cases, this would be where the user submits some long running or queued process. I can’t use the userIDToken as it might expire by the time the cloud function or other process executes, and I don’t really want to start storing firebase user IDS or using some kind of service accounts in all processes that might need to use this capability. So for server based functions that need to run processes on behalf of previously logged on users, the code we’ve just looked at also supports the idea of proxying.

How does proxying work

  • A logged on user will request, via a client mutation request to the API Graphql server, that the API should queue up a long running task to happen at some point.
  • That mutation will publish to a pubsub topic a message which will include the firebase user id of the user that made the request.
  • The server side process (a kubernetes deployment, a cloud function, or a cloud run container) that has subscribed to that topic will act on behalf of the user, making proxy requests to the graphql api using an apikey that allows proxies, and passing the uid in the request header

API receives a proxy request

In this case the x-fid-idtoken header will not be used

But to be able to get the user information from firebase, the api can convert that to a custom JWT and then verify it as in this snippet

Using this function

Generating a custom ID token

The firebase admin sdk can create a custom JWT token, but this token is not decodable as a userIDToken by verifyUserToken, so you need to have the google identity api (enable it in your project first), do that for you as per this post. Thereafter you can treat the token just as if firebase had created a userIDToken

The firebase apiKey is the api key for my firebase project which you’ll find in your firebase console, and the url for the token converter is

Conclusion

Firebase (and Google cloud identity) is pretty awesome in taking care of the hard work of auth, gets rid of the responsibility of keeping databases of passwords and email addresses, avoids all the complications of cookie and session management in a stateless API environment and is still flexible enough to allow you to find ways of doing stuff like this.