Firebase Realtime Database demo (auth with facebook, database actions and security check)

Rex Tsai
DeepQ Research Engineering Blog
6 min readFeb 24, 2018

https://firebase.google.com/docs/database

Firebase is a mobile and web application development platform developed by Firebase, Inc. in 2011, then acquired by Google in 2014.

Store and sync data with our NoSQL cloud database. Data is synced across all clients in realtime, and remains available when your app goes offline.

Following are demos within the topic by Android App client, you need some basic android client or java skills to understand those codes.

Starting

  1. Firebase supply some pricing plan included a free one. In the Firebase console, create a Firebase project, enable Facebook sign-in authentication method and add an Android App to the project. We got package name, “google-services.json” file.

2. Prepare a Facebook App with Client Auth Login at https://developers.facebook.com. We got “App ID” and “App Secret”.

3. Create the App with Android Studio with correct package name and “Bottom Navigation Activity” template, reference to Set up Firebase Realtime Database for Android.

Android App preparing

  1. drag “google-services.json” into app directory.
  2. add dependence libraries to “build.gradle”.
dependencies {
...
compile("com.google.android.gms:play-services-auth:11.4.2") {
exclude group: 'com.android.support'
}
compile 'com.google.firebase:firebase-database:11.4.2'
compile 'com.firebaseui:firebase-ui-auth:3.1.0'
compile 'com.facebook.android:facebook-android-sdk:4.5.0'
}

apply plugin: 'com.google.gms.google-services'

Auth with facebook

Firebase supply many kinds of sign in method. Include email, phone, google, facebook, twitter, Github, anonymous.

After successful login, we can get the user’s basic profile with an unique id, that represent to the user, and totally uniquely to each kind of firebase authenticate types.

put facebook App info to strings.xml

Associated with facebook login, we have follow codes.

private static final int RC_SIGN_IN = 123;
/*
* trigger Facebook login process
*/
List<AuthUI.IdpConfig> providers = Arrays.asList(
new AuthUI.IdpConfig.Builder(AuthUI.FACEBOOK_PROVIDER).build());
// Create and launch sign-in intent
startActivityForResult(
AuthUI.getInstance()
.createSignInIntentBuilder()
.setAvailableProviders(providers)
.build(),
RC_SIGN_IN);
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RC_SIGN_IN) {
if (resultCode == RESULT_OK) {
// Successfully signed in
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
Log.d("RC_SIGN_IN RESULT_OK", user.getUid() + " " + user.getEmail());
} else {
// Sign in failed, check response for error code
// ...
}
}
}

Realtime Database

The Firebase Realtime Database is a cloud-hosted database. Data is stored as JSON and synchronized in realtime to every connected client. Clients get realtime database events in less than 1 second.

Realtime Database between clients

When data is created/deleted/modified, we can saw realtime animate actions in different colors in firebase console DATA section.

Structure Data

JSON tree, up to 32 levels deep
Key customize or by getKey(), maximum of 768 bytes

Supported Data Types

how to Read

// Read a message from the database /alerts/login_alert
FirebaseDatabase database = FirebaseDatabase.getInstance();
mDatabase = database.getReference("alerts").child("login_alert");
mDatabase.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
// This method is called once with the initial value and again
// whenever data at this location is updated.
Log.d("got login_alert", dataSnapshot.getValue().toString());
}
@Override
public void onCancelled(DatabaseError databaseError) {

}
});

how to Write

// Write a message to the database
FirebaseDatabase database = FirebaseDatabase.getInstance();
mDatabase = database.getReference("message");
mDatabase.setValue("Hello, World!");

how to Delete

// Delete "message" node of the database
FirebaseDatabase database = FirebaseDatabase.getInstance();
mDatabase = database.getReference("message");
mDatabase.removeValue();
//mDatabase.setValue(null);

how to Store object

With nice defined object (class), can write to firebase directly.

public class User {

public String username;
public String email;
public long time;

public User() {
// Default constructor required for calls to DataSnapshot.getValue(User.class)
}

public User(String username, String email) {
this.username = username;
this.email = email;
this.time = System.currentTimeMillis();
}

}
private void writeNewUser(String userId, String name, String email) {
User user = new User(name, email);
mDatabase.child("users").child(userId).setValue(user);
}

“push” to getKey and multiple updates

This example uses push() to create a post in the node containing posts for all users at /posts/$postidand simultaneously retrieve the key with getKey(). The key can then be used to create a second entry in the user's posts at /user-posts/$userid/$postid.

private void writeNewPost(String userId, String username, String title, String body) {
// Create new post at /user-posts/$userid/$postid and at
// /posts/$postid simultaneously
String key = mDatabase.child("posts").push().getKey();
Post post = new Post(userId, username, title, body);
Map<String, Object> postValues = post.toMap();

Map<String, Object> childUpdates = new HashMap<>();
childUpdates.put("/posts/" + key, postValues);
childUpdates.put("/user-posts/" + userId + "/" + key, postValues);
mDatabase.updateChildren(childUpdates);
}

How to query

We can query data with specific node tree, sort data with orderByChild(), orderByKey(), orderByValue(), and filter data with limitToFirst(),limitToLast(),startAt(), endAt(), equalTo().

orderByKey() is sorting by node Key. orderByChild() can sort by child’s children data. orderByValue() is good to use if array or plain data.

Realtime Database parts

We store objects named Activity to path /activities/uid/key/activity by follow content.

SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//test write activities
Activity act = new Activity("ROOM", "firebase demo", dateFormat.format(new Date()));
mDatabase = database.getReference("/activities");
mDatabase.child(uid).push().setValue(act);

To explain realtime database actions, when we trying to read data, we need to know follow kinds Listen ValueEvent: addListenerForSingleValueEvent, addValueEventListener, addChildEventListener. And remeber to call removeEventListener when needed.

addListenerForSingleValueEvent: only read once, and no need to call removeEventListener.

addValueEventListener and addChildEventListener will create callback Listener. The Listener will be called when data changed or child added/changed/removed/moved.

FirebaseDatabase database = FirebaseDatabase.getInstance();
mDatabase = database.getReference("/activities/" + uid);
Query queryActivities = mDatabase.orderByKey();
queryActivities.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
// This method is called once with the initial value and again
// whenever data at this location is updated.
for(DataSnapshot ds : dataSnapshot.getChildren()){
Activity act = ds.getValue(Activity.class);
}
@Override
public void onCancelled(DatabaseError databaseError) {

}
});
queryActivities.addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
Activity act = dataSnapshot.getValue(Activity.class);
Log.d("onChildAdded", act.title1 + "@" + act.title2);
}

@Override
public void onChildChanged(DataSnapshot dataSnapshot, String prevChildKey) {
Activity act = dataSnapshot.getValue(Activity.class);
Log.d("onChildChanged", act.title1 + "@" + act.title2);
}

@Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
Activity act = dataSnapshot.getValue(Activity.class);
Log.d("onChildRemoved", act.title1 + "@" + act.title2);
}

@Override
public void onChildMoved(DataSnapshot dataSnapshot, String prevChildKey) {
Activity act = dataSnapshot.getValue(Activity.class);
Log.d("onChildMoved", act.title1 + "@" + act.title2);
}

@Override
public void onCancelled(DatabaseError databaseError) {}
});

Security checks

Secure User Data

Firebase supply Security Rules function to let user controls permissions of Read/Write data, and Validate to help validate data when adding/updating data.

To write rules, the auth variable represent user’s information. This information includes their unique identifier (uid) as well as linked account data. “$key1” represent node key value.

Explain some rules.

message: can read/write if authenticated

activities/user_id: can read/write if authenticated and user self owned

alerts: only read to all users.

{
"rules": {
"message" : {
".read": "auth != null",
".write": "auth != null"
},
"activities": {
"$user_id": {
".validate": "newData.hasChildren()",
"$key1": {
".validate": "newData.hasChildren()",
"type": {
".validate": "newData.isString() && newData.val().matches(/^ROOM|CAMP$/)"
},
"title1": {
".validate": "newData.isString()"
},
"title2": {
".validate": "newData.isString()"
},
"time": {
".validate": "newData.isNumber()"
},
"status": {
".validate": "newData.isString() && newData.val().matches(/^EMPTY|BOOKED|RESERVED$/)"
},
".read": "auth != null && auth.uid == $user_id",
".write": "auth != null && auth.uid == $user_id"
},
".read": "auth != null && auth.uid == $user_id",
".write": "auth != null && auth.uid == $user_id"
}
},
"alerts" : {
".read": true,
".write": false
},
}
}

Rules Simulator

The console supply a rule simulator let you practice your rules setting.

It told you simulated trace details too.

Tool for Firebase security Rules

Targaryen is one of tools that can help us to create/test and generate rule.json. And then with Bolt + Targaryen , you can do more.

--

--