Use Firebase with Google Apps Script

INTRO

Why you might want to do this is not obvious, given that there are already at least two methods (firebase component or REST API with web component) to manage firebase realtime database data, however, it provides a couple of different routes for authentication / secure rules that may be more compatible with your app, and it may also simplify firebase usage.

You can authenticate with a firebase project, using google apps script, with one of two approaches:

Setting up the Google Apps Script Web App

PART 1

Let us first setup an environment with no rules or authentication. We will need a FirebaseApp library and also ensure that our appsscript.json is correctly formatted.

FirebaseApp Library ID = "1hguuh4Zx72XVC1Zldm_vTtcUUKUA6iBUOoGnJUWLfqDWx5WlOJHqYkrt"

In the script editor, click on the + next to Libraries


A popup box opens, copy the ID above, and paste this into the ScriptID textbox, then click on Look up

Then click on Add. Your library will be added, you can see it below "Libraries". (it is best to select the latest version number for your library)

Now click on the cog at the bottom of the icon list on the left. This will open Project Settings

Tick the checkbox to show "appsscript.json", then return to the editor

Your appsscript.json file should look like this. You may have a different time zone, or a different version number for the library

Now, in your project editor, delete whatever is there and replace it with this, obviously using your own firebaseUrl;


var firebaseUrl = "https://fb-gas-default-rtdb.firebaseio.com/";

var base = FirebaseApp.getDatabaseByUrl(firebaseUrl);


function doGet(e) {

if (e.parameter.func == "allRecords") {

var records = JSON.stringify(base.getData());

return ContentService.createTextOutput(records);

}

}


Now you can deploy your project as a web app and get the script url


Create a simple AI2 app, with a button and a label and drag in a web component. Setup your blocks like so:

Run in companion app, and then press on the Return All Data button, and you should see a json of all your data - you can then work on this using the dictionary blocks:

If we wanted to return just a single record, then we would need to add another function to the script and write some more blocks:

if (e.parameter.func == "readRecord") {

var record = JSON.stringify(base.getData(e.parameter.pb + "/" + e.parameter.key));

return ContentService.createTextOutput(record);

}

and let us return the record for John Smith:

Let us add a record now. We will use POST for this, so we need to add a doPost(e) function to our web app. Notice in the blocks that we set some parts as parameters, and other parts in a json. Once you get the hang of this, you will be up and down the firebase data tree like a monkey :)

function doPost(e) {


if (e.parameter.func == "addRecord") {

var recordData = JSON.parse(e.postData.contents);

base.setData(e.parameter.pb +"/" + e.parameter.key, recordData);

return ContentService.createTextOutput("new record for " + e.parameter.key + " added to Project Bucket " + e.parameter.pb + " on firebase"); 

}

To update a record you can use the updateData() method with POST, this helps to avoid overwriting any existing data. To delete a record you can use either the removeData() method, or use setData() and set an empty json - {}. pushData() will create a record but generate a unique id. getAllData() will return the data for records at specified paths. You can also run a query, but you may need some indexing on the data to be able to do this.

The FirebaseApp provides the following commands:

getData(path, optQueryParameters)
getAllData(requests)
pushData (path, data, optQueryParameters)
setData(path, data, optQueryParameters)
updateData(path, data, optQueryParameters)
removeData(path, optQueryParameters)

PART 2

Using the Firebase Project Secret Key to Authenticate

This is where the fun starts, and provides the AI2 app developer with fine control within the app over who can access what data. we use the Database secrets key, along with the FirebaseApp library, to give the equivalent of firebase console access to google apps script, and consequently the AI2 app. The first step is to change the read/write rules to false. No-one outside of being able to use the AI2 app or the google apps script can access the data once this is done. Developers will want to include controls in the AI2 app, and google apps script, to ensure only those trusted or allowed access, have access. You do not really want to make your secret key available to anyone.

Firstly, change the rules:

Then, click the cog next to Project Overview, then click on Project Settings

Insert the following lines at the top of your script, under the "var base = ...." line

var secret = "LGJ43O92PsOGbPSRD8fbAzxe2kGAxknbeh1hljmA"; << this is a fake secret key...

var baseSecret = FirebaseApp.getDatabaseByUrl(firebaseUrl,secret);

Now, look through your code and find the lines that reference the variable "base". You need to replace "base" with "baseSecret". For example, change:

if (e.parameter.func == "allRecords") {

var records = JSON.stringify(base.getData());

return ContentService.createTextOutput(records);

}

to this:

if (e.parameter.func == "allRecords") {

var records = JSON.stringify(baseSecret.getData());

return ContentService.createTextOutput(records);

}


Re-deploy your web app to a new version. You should now be able to run your AI2 app as before, getting and setting data, even though the rules are set to false. You have god powers over your firebase data from your app! Yet no-one outside of your app can get at the data. You will obviously need to put some controls in place to ensure that only trusted users have this ability, and that your app does not allow full access (e.g. DELETE) to all data.

PART 3

Using a service account to generate a user token to Authenticate

This method has an end result much the same as the one in PART 2, although it does generate a different userToken for each access, but the service account's private key is needed, which again, you do not really want to share. it may be better to store it in Script.Properties.. The setup is also a little more complex. Make sure you have at least read through PART 2, which explains and shows images of some of the actions required.

As previously mentioned, the private key of your service account should never be shared. In the example above, it is saved directly as a global variable in the source code but you can decide to store it somewhere else (eg: in a Script Property).