Search code examples
firebasegoogle-apps-scriptfirebase-realtime-databasefirebase-authenticationfirebase-security

Firebase Database Secrets on Google Apps Script


So, first of all, I'm asking this question because I want that someone admits or denies -whats for me is a myth- about to use or not to use the deprecated firebase database secrets.

Why this? Well, in the Service Accounts tab from the Firebase console, I can read this:

Databse secrets are currently deprecated ans use a legacy Firebase token generator. Update your source code with the Firebase Admin SDK

But belows this advice, I can currently see my secret key. Why is this possible, if the use of this secret key is actually deprecated?

But this story, does not ends here. If a search for tutorials using Google Apps Script (even on officials), they use the deprecated database secret key, but you can read this:

Google warns that this authentication method is deprecated. In fact you can still use this method but standard Service Accounts are more secure and are recommended. As Database secrets are easier to use, far from really deprecated and safe enough for most use cases, that's what we will use here.

Again, how is this possible?

I want to say that I have tried:

function myFunction() {
  var firebaseUrl = "https://myApp.firebaseio.com/";
  var secret = "theKey";
  var database = FirebaseApp.getDatabaseByUrl(firebaseUrl, secret);
  Logger.log(database.getData());
}

and it works, because I cann see the data. Why if the secret is present? This brings me to think that the use of the secret key is allowed, even it is deprecated

Any opinions?


Solution

  • So I had this issue a full year and a half later, and you are right, the information out there for using Firebase with Apps Script is outdated and misleading. I figured it out and am trying to update the information in StackOverflow and the tutorials.

    But this story, does not ends here. If a search for tutorials using Google Apps Script (even on officials), they use the deprecated database secret key, but you can read this...

    This link you added here is actually a 3rd party website by a dude named @RomainVialard. His stuff has been very useful to me in the past. While the tutorials on his website have gotten outdated, the library he created still functions wonderfully and was updated semi-recently.


    The Issue (Server-side) - Answer to OP's Question

    So for the server-side piece, I used the official-ish OAuth2 Library to generate access tokens from a service account with a private key: https://console.firebase.google.com/u/0/project/<YOUR-PROJECT-ID>/settings/serviceaccounts/adminsdk. I then created a function that retrieves the token from the oauth2 library:

    function getFirebaseService() {  
      return OAuth2.createService('Firebase')
          // Set the endpoint URL.
          .setTokenUrl('https://accounts.google.com/o/oauth2/token')
    
          // Set the private key and issuer. I stored mine in the PropertiesService... You could just use a variable somewhere.
          .setPrivateKey(fb_PRIVATE_KEY) //Service account private key
          .setIssuer(fb_SERVICE_EMAIL) //Service account email
    
          // Set the property store where authorized tokens should be persisted.
          .setPropertyStore(PropertiesService.getScriptProperties())
    
          // Set the scopes.
          .setScope('https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/firebase.database');
    }
    

    With this code, I can then call getFirebaseService().getAccessToken() which returns a valid 'admin' or Editor level access token that can be used in a Firebase call! I use the FirebaseApp library because it's easy!

    var service = getFirebaseService();
      if (service.hasAccess()) {
        var fb = FirebaseApp.getDatabaseByUrl(fb_URL, service.getAccessToken());
        //Success! Do what ever with Firebase!!
    
      } else {
        Logger.log("makeToken: " + service.getLastError());
      }
    

    You can also generate a client authentication token to use client-side if needed: FirebaseApp.getDatabaseByUrl(fb_URL, service.getAccessToken()).createAuthToken(USER_EMAIL);.


    Another Common Issue (Client-side)

    The main problem I had to overcome in my question, was that Firebase Admin SDK has been upgraded to 3.x. If you update the tutorial code to use the 3.x version everything works wonderfully!

    // Initialize Firebase
    var config = {
      apiKey: "<Web API Key>",
      authDomain: "<Project ID>.firebaseapp.com",
      databaseURL: "https://<DB URL>.firebaseio.com/"
      };
    
    firebase.initializeApp(config);
    
    //userRequestToken is retrieved from server-side Romain's FirebaseApp Library linked above.
    firebase.auth().signInWithCustomToken(userRequestToken).catch(function(error) {
        // Handle Errors here.
        console.error("authClient: ", error.code, error.message);
      });
    
      return {
        uid: firebase.auth().currentUser.uid,
        metadata: {
          lastSignInTime: firebase.auth().currentUser.lastSignInTime
        }
      };