Is it still possible to do server side verification of tokens in Firebase 3?
Asked Answered
C

2

46

Is it still possible to do server side verification of tokens in Firebase 3?

We generate custom tokens (JWT) on a server running Golang using our existing authentication system (using a Service Account). The token is used on an iOS client using

FIRAuth.auth()?.signInWithCustomToken(customToken)

Until there it all works fine. But when we pass the client token to the server retrieved from:

FIRUser.getTokenWithCompletion({ token, error in ..})

we're not able to verify it. The JWT token is signed using RS256 and has an header.kid we can't recognize. The public key from the Service Account (which was used to sign the custom token) doesn't verify the client token. Is the public key needed to validate the client token available?

I know it's possible to validate client tokens using the "verifyIdToken" call in Java or Javascript, but we hope to stil be able to do this in Golang using a standard JWT library.

This all worked fine in Firebase 2 (using HS256 and the Firebase secret).

Collinsworth answered 24/5, 2016 at 8:53 Comment(5)
Hi Tim, hang in there. I have most of this documented now. Working out some details with the experts this afternoon and then I'll post a fairly authoritative reply here.Kyongkyoto
Thanks Kato, that's great to hear!Collinsworth
I've updated the answer and added some Go solutions as well. I'll work on getting this into the official docs for others.Kyongkyoto
Thanks a lot Kato! Much more than I hoped, your answer really helps in choosing the right strategy for securely interacting with the server logic.Collinsworth
View #38188622Subscription
K
53

The short answer is yes. The full answer is that, in most cases, we have a more appropriate tool now. So a lot depends on the use case you are trying to resolve.

The new SDK version is quite a bit more powerful, and we haven't done a great job of summarizing the capabilities. This seems like a good place to contrast the tools available and their uses, and then I'll tack on some third-party (i.e. Go) specific notes at the end.

Using an external authentication tool for client authentication

The primary use of minting custom tokens is to allow users to authenticate against an external/legacy auth mechanism you control, such as your LDAP server. The basic process for this is covered here: iOS, Android, Web.

Essentially, your service just mints the JWT token and passes this to the client. The client does the verification/authentication using the custom token you provide.

Authenticating your privileged workers

It's no longer necessary to use custom tokens to authenticate your server process. This is done by creating a service account, which is covered step-by-step in Adding Firebase to your Server. When done, you'll end up with a JSON file that contains a private key.

Then, you include your service account credentials by referencing that JSON using the serviceAccount attribute in firebase.initializeApp(), and you're in! That's documented here and looks like this (see link for Java version):

var firebase = require("firebase");

// Initialize the app with a service account, granting admin privileges
firebase.initializeApp({
  databaseURL: "https://databaseName.firebaseio.com",
  serviceAccount: "./serviceAccountCredentials.json"
});

Emulating users or limiting access from a server process

It's fairly trivial to emulate a user or to limit access (highly recommended) from a server process. You don't really need to mint a custom token for this anymore.

This just requires adding the databaseAuthVariableOverride into your call to database.initializeApp():

firebase.initializeApp({
  databaseURL: "https://databaseName.firebaseio.com",
  serviceAccount: "./serviceAccountCredentials.json",
  databaseAuthVariableOverride: {
    uid: "my-service-worker-or-user-uid"
  }
});

Validating client identity via security

First of all, you can usually avoid dealing with server-side verification if you are using Firebase Database, by having your client write to the database and using security rules to validate their identity. If your server listens on a path that requires authentication to write into, then this is already solved without any special security at the server.

By modeling this as an event queue, it creates a simple, modular, and scalable server worker strategy. See firebase-queue for some great Node.js tools. It supports 3.x.

Verifying client ID tokens on the server

If you aren't using the Realtime Database and need to receive client tokens (e.g. via REST calls) and verify that they are valid, you can do so by using verifyIdToken() as described here. This would look like the following:

auth.verifyIdToken(idToken).then(function(decodedToken) {
  var uid = decodedToken.sub;
});

If you then want to authenticate as that user to write to the database and enforce security, you would use the Emulating Users section above. In other words, call initializeApp() with a databaseAuthVariableOverride set to the appropriate uid.

Note that, if you try to call initializeApp() multiple times and run into an error similar to the following: Error: Firebase App named '[DEFAULT]' already exists. You can initialize multiple app contexts by adding a second argument to the initializeApp() call (e.g. database.initializeApp({...}, 'asUser'+uid)) and then reference that app instance by using firebase.database('asUser'+uid).ref(...). To read more on using multiple app instances, look here.

Java code available at the links above. Go and other third party solutions covered below.

Creating a token for use in the REST API

Michael Bleigh covered this scenario here and deserves some rep for working this out.

Creating tokens or verifying them via REST

This isn't supported. Sorry.

Golang and others: More to come

We're working on a Go token minting and verification library. We'll also be adding Python tools for this soon as well. No release date or ballparks for this. In the mean time, if you'd like to verify client ID tokens without using the official Firebase Node.js or Java libraries (which have built-in verification methods), you will need to ensure the ID token (which is a JWT) conforms to the following:

  • Its decoded header has an alg (algorithm) claim equal to "RS256".
  • Its decoded payload has an aud (audience) claim equal to your Firebase project ID.
  • Its decoded payload has an iss (issuer) claim equal to "https://securetoken.google.com/<projectId>".
  • Its decoded payload has a non-empty string sub (subject) claim. Note that this is the uid for that Firebase user.
  • Its decoded header has a kid (key ID) claim that corresponds to one of the public keys listed at https://www.googleapis.com/robot/v1/metadata/x509/[email protected].
  • You also need to use a JWT library to verify the token with the public key to prove the token was signed with the public keys' corresponding private key.

For Go, it looks like you can use jwt-go to decode and validate the client ID token.

Kyongkyoto answered 27/5, 2016 at 21:41 Comment(15)
How often should we check out the public keys? Are they being changed on a daily basis, like every midnight PDT?Becker
There is a inofficial library for Go Token verification now: github.com/alternaDev/go-firebase-verifyWieche
How about for testing, I generate a token (custom so I could set the expire time in fb 3x), and I'd love to know it works in a server side test.Brewhouse
@BigTeddy do you know of a minting library? I have looked at the php/ruby examples... firebase.google.com/docs/auth/server#use_a_jwt_library maybe Im going down the wrong path? I wanted to use fireauth (github.com/zabawaba99/fireauth) but that is doing v2.0 authentication.Allianora
@Allianora depends on what you want to do. If you want to check Tokens coming from Firebase, my library is the right one. If you want to generate your own Tokens as a Custom Auth Provider, you should only need jwt-go to generate the tokens.Wieche
very annoying that 99% of this is for SDK or other custom JS libs and not for REST API.Geostrophic
it's pretty naive to think you will cover all possible server-side languages with your SDKs.Geostrophic
Creating tokens or verifying them via REST: This isn't supported. Sorry. Why is this? Will it be supported in the future?Stonedeaf
You don't really need to mint a custom token for this anymore. How do you access the User this way? Should it be accessible at firebase.auth().currentUser without having to login?Varden
I've written a firebase-server-sdk-go which you can give a try. github.com/wuman/firebase-server-sdk-goSpiffy
In the firebase console I have the private key associated with a service account. But where I can find the public key?Legitimatize
The public key is stored on the Firebase servers. Like your .ssh/ keys in Linux or Mac, you store the private key locally and share the public key with the services you want to connect to. There's not currently a way to download the public key.Kyongkyoto
@Kyongkyoto I am trying for 2 days to get access to my FirebaseDatabase with a service account with no success. Could you please have a look at my question here stackoverflow.com/questions/40810233Diopside
can somebody explain please how to pass public keys to decode function in PHP? Please see my question here: #42098650Lalalalage
@Kato, I get "Custom token format invalid" when I generate token in .net, I was wondering if still Golang and others: More to come is valid, because based on https://firebase.google.com/docs/auth/admin/create-custom-tokens iss,sub and other params are completely different than what you mentioned here. (I create token like what is in the docs)Haleakala
A
0

Well firebase doesn't allow verification of custom tokens what they do is to allow verification of id tokens they are generated once user sign-in using custom tokens. So as was the case with me, if you pass firebase custom tokens to other services to authenticate with your backend then custom token verification onus is on you! Also the google service account X509 cert is not in proper format. It has these \n (new line) delimiters that do not get replaced with new lines in text editors (I use vim). Hence what I did is this:

  val factory = CertificateFactory.getInstance("X.509")
  val certificateFile = this.getClass.getResourceAsStream(Play.current.configuration.getString("firebase.certificate").get)
  val publicKey = factory.generateCertificate(certificateFile).asInstanceOf[X509Certificate].getPublicKey
  val claimBody = Jwts.parser().setSigningKey(publicKey).parseClaimsJws(compactJws).getBody
  1. Get the certificate from google service account link specified in the json downloaded while setting up firebase
  2. Manually replace \n with new line
  3. Get JWT library. I used this great library to do verification Java JWT
  4. Read the certificate and extract out public key.
  5. Use the public key to verify token
  6. Make sure you have another api to refresh tokens since there are only valid for an hour

Hope that helps!

Alcaeus answered 2/6, 2017 at 7:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.