Developing plugins with Firebase

The Firebase JavaScript client v1.0.17 and AngularFire v0.8.0 is available to all plugin developers. Use it to store and sync data in realtime.

This guide covers basic aspects on how to use Firebase in your plugins. For a complete reference or tutorials, checkout the links in the bottom of this page.

Initializing Firebase

After creating an app in Firebase, go to the settings section in the Developer Tools and save the URL and secret of your Firebase app. To initialize Firebase from your plugin frontend, inject the $firebase service in the controllers, services, factories or directives signature, create a reference to your Firebase URL and assign it to the scope:

/**
 * My Plugin Controller
 */
plugin.controller('myPluginCntl', ['$scope', '$firebase', function ($scope, $firebase) {
    
    var peopleRef = new Firebase('https://<my-firebase>.firebaseio.com/people');
    
    $scope.people = $firebase(peopleRef);
    
}])

Adding data to a list

The $add method takes a single argument of any type. It will append this value as a member of a list.

/**
 * My Plugin Controller
 */
plugin.controller('myPluginCntl', ['$scope', '$firebase', function ($scope, $firebase) {
    
    var peopleRef = new Firebase('https://<my-firebase>.firebaseio.com/people');
    
    $scope.people = $firebase(peopleRef).$asArray();
    
    $scope.people.$add({
        name: 'Steve',
        location: 'Philadelphia, US'
    });
    
}])

Adding or Updating keys

The $update method takes a single argument of any type. It will append or update existing keys.

/**
 * My Plugin Controller
 */
plugin.controller('myPluginCntl', ['$scope', '$firebase', function ($scope, $firebase) {

    // Initialize a reference direct to a person using the <id>
    var personRef = new Firebase('https://<my-firebase>.firebaseio.com/people/<id>');

    $scope.person = $firebase(personRef);

    // Add the person's birthday
    $scope.person.$update({
        birthday: '12/10/1980'
    });
    
    // Or change the person's location
    $scope.person.$update({
        location: 'Tokyo, JP'
    });
    
    
}])

Removing data

The $remove method takes a single optional argument, a key. If a key is provided, this method will remove the child referenced by that key.

/**
 * My Plugin Controller
 */
plugin.controller('myPluginCntl', ['$scope', '$firebase', function ($scope, $firebase) {
    
    // A reference direct to a person using the <id>
    var personRef = new Firebase('https://<my-firebase>.firebaseio.com/people/<id>');
    
    $scope.person = $firebase(personRef); 
    
    // Remove the person
    $scope.person.$remove();
    
    // Or remove only the location key
    $scope.person.$remove('location');
    
}])

Authentication

To get advantage of the already logged in user in Zengine and authenticate this same user in Firebase, you need to use the custom login method, this method uses a JWT token instead of a username and password.

You can get a token to authenticate the current user in Firebase by fetching your plugin data using the Data factory. The response will contain a firebaseAuthToken attribute, which is generated using the Firebase secret that you set in your “Plugin Settings”.

The example below demonstrates how you can fetch the current plugin and user data from Zengine API, and then use it to connect to Firebase.

/**
 * My Plugin Controller
 */
plugin.controller('myPluginCntl', ['$scope', 'znData', '$firebase', function ($scope, znData,  $firebase) {

    /**
     * Get the current plugin data
     */
    znData('Plugins').get(
        {
            namespace: 'myPlugin'
        },
        function(resp) {
            $scope.plugin = resp[0];
        },
        function(resp) {
            $scope.err = resp;
        }
    );
    
    /**
     * Get the currently logged-in user data
     */
    znData('Users').get(
        {
            id: 'me',
        },
        function(resp) {
            $scope.user = resp;
        },
        function(resp) {
            $scope.err = resp;
        }
    );
    
    /**
     * Wait for plugin and user data and connects to Firebase
     */
    var unbind = $scope.$watchCollection('[plugin, user]', function() {
    
        // Stop on any errors fetching plugin or user data
        if ($scope.err) {
            return;
        }
        
        // Checks if both plugin and user data is available to connect
        if ($scope.plugin !== undefined && $scope.user !== undefined) {
            $scope.connect();
            unbind();
        }

    });
    
    /**
     * Connects to firebase and authenticate
     */
    $scope.connect = function() {
    
        // Uses the `$scope.user.id` to make a direct reference to the current user preferences
        var ref = new Firebase('https://<my-firebase>.firebaseio.com/preferences/' + $scope.user.id);
        
        // Uses the `$scope.plugin.firebaseAuthToken` to authenticate the user
        ref.auth($scope.plugin.firebaseAuthToken, function(err, res) {
            
            if (err) {
                $scope.err = err;
            }
            
            // Set auth data
            $scope.auth = res.auth;
            
            // Set preferences
            $scope.preferences = $firebase(ref).$asObject();
            
            // Apply changes to the scope
            $scope.$apply();
            
        });
    
    };
    
}])

Security rules

Using the Firebase dashboard, you can setup security rules to define when your data can be read from and written to.

When using Firebase from the frontend Javascript of a plugin, the following data is passed to Firebase and made available with the auth variable in your security rules:

{
    "user_id": "1",
    "workspaces": {
        1: "admin",
        2: "owner",
        3: "owner",
        4: "member"
    }
}

The workspaces object contains all of the workspaces the currently authenticated user is a member of, where each key is the workspace id and the corresponding value is the user’s role in that workspace.

Now we’ll show how you can use this auth variable in your security rules. In the example below, the reference https://<my-firebase>.firebaseio.com/preferences/<user-id> is a list of preferences of a specific user id. The rules below allow read and write access only if the currently authenticated user id matches the <user-id>.

{
    "rules": {
        "preferences": {
            "$user": {
                ".read": "$user == auth.user_id",
                ".write": "$user == auth.user_id"
            }
        }
    }
}

Another example is restricting read access to any user belonging to the workspace and write access to workspace owners, assuming the following reference to https://<my-firebase>.firebaseio.com/preferences/<workspace-id>

{
    "rules": {
        "preferences": {
            "$workspace": {
                ".read": "auth.workspaces[$workspace] != null",
                ".write": "auth.workspaces[$workspace] != null && auth.workspaces[$workspace] == 'owner'"
            }
        }
    }
}

When using the znFirebase wrapper with a Backend service, znFirebase will automatically authenticate and the following data will be available with the auth variable in your security rules, assuming the request was made to a backend service at https://plugins.zenginehq.com/workspaces/1/testBackendService/testRoute:

{
    "workspaces": {
        1: "server"
    }
}

Similar to the authentication data from the frontend, the auth data from the backend contains a workspaces object. However, for backend service Firebase authentication, the workspaces object contains a single key-value pair: the workspace id which the backend service request was made against and the value server.

With this, you can extend your security rules to also allow backend service access. We will expand on the previous example to also allow read and write access to backend services:

{
    "rules": {
        "preferences": {
            "$workspace": {
                ".read": "auth.workspaces[$workspace] != null",
                ".write": "auth.workspaces[$workspace] == 'owner' || auth.workspaces[$workspace] == 'server'"
            }
        }
    }
}

Learn more about Firebase security rules.