Firebase Authentication with a custom Dart Server Application
[originally posting here]
This post describes my experiments using Firebase Authentication with a Dart Server application (for example, a shelf based web app). The scenario is that you have a client application (a Flutter Web app, for example) that you want to authenticate using Firebase, but the backend is your own custom web application instead of hosted Firebase services.
Firebase Authentication is essentially free, and offers social login with all the usual suspects in addition to username/password and phone number authentication. Why roll your own IAM when you can get it for free?
There are examples of this hybrid scenario for other languages where there is a Firebase admin SDK, but I couldn’t find detailed notes on how to do this using Dart on the backend.
Before we get to the solution (which turns out to be fairly simple), a quick detour into some of the workings of Firebase AuthN.
Once Firebase successfully authenticates your client, it issues it an JWT Identity Token (idtoken).
To play around with this, I started off by forking the example Flutter Fire Authentication Sample application. I modified the application slightly to dump the idtoken for inspection. Once you authenticate with firebase, cut and paste the idtoken into https://jwt.io. You will see something like:
You will see “invalid signature” on this jwt. We can resolve that by fetching the JWK for the Firebase Authentication provider. This wasn’t totally obvious, but the JWK can be fetched from:
https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com
Grab the Json object with the Key Id (kid) that matches the kid found in the idtoken header. Paste that into the jwt.io “Verify Signature” box, and you will find the signature is now verified. In other words, the idtoken was issued and signed by Firebase.
How do we do this with our Dart server application? Thanks to the nifty openid_client package, this turns out to be surprisingly easy. This package features built in support for the Firebase Authentication provider. Here is an example with comments:
To recap the general idea:
- Your client application is configured to use Firebase authentication.
- Once the Firebase authenticates the user, your client sends the issued idtoken to your Dart server (note: You need to be aware of CORS concerns here).
- Your server uses the openid_client package to validate the token passed by the client. If the token checks out, you can assume the user has a valid session with firebase. The claims can be used in your application to identify the user. For example, the firebase user_id uniquely identifies the user, and can be used as a primary key in your database.