Recently our team received a task to integrate user authorisation via Firebase phone auth. A quick investigation of Firebase product directed us to this article about Verify ID Tokens. If you have a Firebase client app which communicates with a custom backend server, you might need to identify the currently signed-in user on that server. To do so securely, after a successful sign-in, send the user’s ID token to your server using HTTPS. Yes, after successful login into Firebase each user receives his own ID token which is actually a JWT token. Then, on the server, verify the integrity and authenticity of the ID token and retrieve the uid from it. You can use the uid transmitted in this way to securely identify the currently signed-in user on your server.
Enough theory. So we started this task, but immediately realised that Firebase do not have the Firebase Admin SDK for Ruby. It’s only for Node.js, Java and Python. D’oh! For guys like us they have a separate section Verify ID tokens using a third-party JWT library , something like DIY. Alright, we took ruby-jwt gem and started our journey.
We spend about 2 days to make it fully workable, so mainly it went smooth. But I would like to share some pit fails with you that are not so obvious when you start.
First of all, you can use my shared web page to sign in via Firebase phone number and obtain the ID Token (they call it accessToken in JSON). If you want you also can setup your own web page, luckily Firebase provides a lot of ready-to-go code samples.
So now you have an accessToken, let’s try to verify it.
Your server API has to receive 2 input parameters: access token and Firebase project ID
The first thing is that if you build REST API, e.g. sign in with Firebase phone, then you need to obtain 2 parameters from client: accessToken and also Firebase project ID. Firebase project ID is used in 2 validations (aud and iss payload parameters), so your server needs it in order to do a proper validation.
Grab public keys from Google server periodically, to validate token signature
The second thing is that you need periodically grab valid public keys. You need these keys to validate your token’s signature and the kid header. Luckily, Google returns the Cache-Control header with max-age param, so we can cache response for some time.
To properly validate your token signature you need to decode it twice
The last but very important thing is that you need to call JWT.decode method twice to validate your token signature properly. After first time you decode your token and obtain headers and payload. Then you take the kid header and obtain a certificate from step 2 which corresponds to this particular kid header. And only then you call again the JWT.decode method where you pass now this public key to decode and validate signature.
You can read an official response from the ruby-jwt maintainers .
That’s all I want to share with you. Full code of ruby class-verifier you can find in my GitHub. Ask me if you have any issues.