8

I'm migrating to the new database and 3.0 client libs. I'm updating the part which generates a custom auth token (on our server) to do a PATCH to update a resource in the Firebase DB.

These PATCH requests used to be made by our server to Firebase using admin claims based on this: https://www.firebase.com/docs/rest/guide/user-auth.htm

For the new DB, I'm generating the JWT token (using ruby-jwt) like this:

payload = {
  aud: "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
  claims: custom_claims.merge({ admin: true }),
  exp: now_seconds + (60 * 60), # Maximum expiration time is one hour
  iat: now_seconds,
  iss: service_account_email,
  sub: service_account_email,
  uid: uid
}

JWT.encode(payload, private_key, "RS256")

A PATCH request with this token to the Firebase DB fails with: Missing claim 'kid' in auth header.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Akshay Rawat
  • 4,714
  • 5
  • 40
  • 65

2 Answers2

16

In the new Firebase you need to directly use a Service Account to create administrative access credentials. Here is a Node.js snippet that shows how to make a REST call to the Database:

// key.json is a service account key downloaded from the Firebase Console
var key = require('./key.json');

var google = require('googleapis');
var request = require('request');

var DATABASE_URL = 'https://<databaseName>.firebaseio.com';

var jwtClient = new google.auth.JWT(key.client_email, null, key.private_key, [
  'https://www.googleapis.com/auth/userinfo.email',
  'https://www.googleapis.com/auth/firebase.database'
]);

jwtClient.authorize(function(err, tokens) {
  request({
    url: DATABASE_URL + '/.json',
    method: 'GET',
    headers: {
      'Authorization': 'Bearer ' + tokens.access_token
    }
  }, function(err, resp) {
    console.log(resp.body);
  });
});

To do the same in Ruby, you might take a look at the googleauth gem for fetching the access token using Service Account credentials.

Michael Bleigh
  • 25,334
  • 2
  • 79
  • 85
  • Is it possible to include custom claims, accessible from my database rules, like with Firebase 2.x custom REST tokens? – user1881056 Jun 07 '16 at 19:35
  • You can add custom claims with the `auth_variable_override` query parameter (just make it a valid JSON object). – Michael Bleigh Jun 09 '16 at 19:47
  • You mean as a query parameter in the REST request itself? How is that secure? My client is untrusted (an IoT device) and I need a secure way to limit its access to the Firebase DB. In fact, this (apparently undocumented) feature blows a whole in my entire security model. I hope I've misunderstood you! – user1881056 Jun 09 '16 at 21:47
  • You should **absolutely not** use a service account to authorize an untrusted IoT device; I misunderstood your use case. The 3.x SDKs are lacking a Node.js solution for this at the moment, we recommend you use 2.x SDK for now and we'll have a solution coming out in the near future. – Michael Bleigh Jun 11 '16 at 01:50
  • Thanks for clarifying. I look forward to a solution soon. – user1881056 Jun 12 '16 at 23:44
  • Is it possible to set the debug flag for auth tokens when using this method? I tried setting it in the jwt claims as well as in `auth_variable_override`, but no cigar. – ibash Aug 08 '16 at 20:22
  • @MichaelBleigh Why is it necessary to include `https://www.googleapis.com/auth/userinfo.email` in scopes when we only want to read/write firebase database from a client web app using a service account? [Please see my question furtherer](http://stackoverflow.com/questions/40810233) – bibscy Nov 27 '16 at 15:49
1

Here is the equivalent of Michael Bleigh's answer using the ruby googleauth module:

require 'googleauth'

scopes = [ 'https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/firebase.database']
auth = ::Google::Auth.get_application_default(scopes)
auth_client = auth.dup
auth_client.sub = "service-account-email-here@yourapp.iam.gserviceaccount.com"
token = auth_client.fetch_access_token!

You will also need to set the GOOGLE_APPLICATION_CREDENTIALS environment variable to the path of your service account JSON file. the value for auth_client.sub comes from client_email in this JSON file.

Of course, as above, this is only valid in a server application you control.

Also, making the request to the firebase REST API is still an exercise for the reader.

references