5

I wrote following simple presence code in JavaScript (based upon https://firebase.google.com/docs/database/web/offline-capabilities#section-sample):

var app = firebase.initializeApp(config);
var mainRef = app.database().ref();
var session = null;
var connected = false;

function do_sessionSubscribe(subscription) {
    if (!subscription.entry) {
        subscription.entry = subscription.parent.push(true);
        subscription.entry.onDisconnect().remove();
    }
}

function do_sessionUnsubscribe(subscription) {
    if (subscription.entry) {
        subscription.entry.remove();
        subscription.entry = null;
    }
}

mainRef.child(".info/connected").on("value", function(snap) {
    connected = snap.val() === true;
    if (session) {
        if (connected) {
            do_sessionSubscribe(session.subscription);
        } else {
            // workaround
            //do_sessionUnsubscribe(session.subscription);
        }
    }
});

function closeSession() {
    if (session) {
        do_sessionUnsubscribe(session.subscription);
        session = null;
    }
}

function openSession(uid) {
    session = { uid: uid, subscription: { parent: mainRef.child("session/user/" + uid), entry: null } };
    if (connected) {
        do_sessionSubscribe(session.subscription);
    }
}

app.auth().onAuthStateChanged(function(user) {
    closeSession();
    if (user && user.uid) {
        openSession(user.uid);
    }
});

Security rules:

"session": {
    "user": {
        "$uid": {
            ".read": "auth.uid === $uid",
            ".write": "auth.uid === $uid",
            "$session": {
                ".validate": "newData.val() === true"
            }
        }
    },
}

The idea is that each active connection of a user will create /session/user/$uid/$session upon connecting/signing in and delete it when disconnecting/signing out.

Therefore in order to obtain a list of online users it should be sufficient to get /session/user with shallow=true.

The problem is that sometimes a session isn't cleaned up and stays under /session/user/$uid forever. This is then interpreted like if a user was online all the time.

I discovered that in order to easily reproduce the issue it is sufficient to block access to securetoken.googleapis.com (I use Google authentication), wait an hour and close the browser.

I tried to workaround the problem by calling remove() on disconnection. This cleans up the stale session as soon as the client gets reconnected (this is too late, but better late than never...). However, when user closes it's browser after loosing internet connection and then the auth token expires before sockets time out, the stale session persists forever.

  1. What value of auth.uid is used during checking security rules when auth token used for registering onDisconnect() action is already expired?
  2. How to make this presence system fully reliable without compromising security?
Firefox OS
  • 63
  • 4

0 Answers0