Search code examples
androidfacebookcouchbasecouchbase-litecouchbase-sync-gateway

Couchbase facebook pull authenticator


I am using couchbase mobile for an application and I want to use facebook for authentication. As per documentation, couchbase offers it's own implementation for authentication, the only required thing would be the token which I retrieve from the android facebook login flow.

The code for Synchronize class looks something like this:

public class Synchronize {

public Replication pullReplication;
public Replication pushReplication;

public static class Builder {
    public Replication pullReplication;
    public Replication pushReplication;

    public Builder(Database database, String url, Boolean continuousPull) {

        if (pullReplication == null && pushReplication == null) {

            URL syncUrl;
            try {
                syncUrl = new URL(url);
            } catch (MalformedURLException e) {
                throw new RuntimeException(e);
            }

            pullReplication = database.createPullReplication(syncUrl);
            pullReplication.setContinuous(true);

            pushReplication = database.createPushReplication(syncUrl);
            pushReplication.setContinuous(true);
        }
    }

    public Builder facebookAuth(String token) {

        if (!TextUtils.isEmpty(token)) {
            Authenticator facebookAuthenticator = AuthenticatorFactory.createFacebookAuthenticator(token);

            pullReplication.setAuthenticator(facebookAuthenticator);
            pushReplication.setAuthenticator(facebookAuthenticator);
        }

        return this;
    }

    public Builder basicAuth(String username, String password) {

        Authenticator basicAuthenticator = AuthenticatorFactory.createBasicAuthenticator(username, password);

        pullReplication.setAuthenticator(basicAuthenticator);
        pushReplication.setAuthenticator(basicAuthenticator);

        return this;
    }

    public Builder addChangeListener(Replication.ChangeListener changeListener) {
        pullReplication.addChangeListener(changeListener);
        pushReplication.addChangeListener(changeListener);

        return this;
    }

    public Synchronize build() {
        return new Synchronize(this);
    }

}

private Synchronize(Builder builder) {
    pullReplication = builder.pullReplication;
    pushReplication = builder.pushReplication;
}

public void start() {
    pullReplication.start();
    pushReplication.start();
}

public void destroyReplications() {
    if (pullReplication != null && pushReplication != null) {
        pullReplication.stop();
        pushReplication.stop();
        pullReplication.deleteCookie("SyncGatewaySession");
        pushReplication.deleteCookie("SyncGatewaySession");
        pullReplication = null;
        pushReplication = null;
    }
}

}

And I use it like this:

...
public void startReplicationSync(String facebookAccessToken) {

    if (sync != null) {
        sync.destroyReplications();
    }

    final String url = BuildConfig.URL_HOST + ":" + BuildConfig.URL_PORT + "/" + DATABASE_NAME;
    sync = new Synchronize.Builder(databaseManager.getDatabase(), url, true)
            .facebookAuth(facebookAccessToken)
            .addChangeListener(getReplicationChangeListener())
            .build();
    sync.start();

}
...

My sync gateway json config file:

{
"interface":":4984",       
"adminInterface":":4985",
"log":["REST"],
"facebook":{
  "register" : true
},
"databases":{               
"sync_gateway":{
"server":"http://localhost:8091",
"bucket":"sync_gateway",
"users": {
    "GUEST": {"disabled": false}
},
"sync":`function(doc) {channel(doc.channels);}`
}
}
}

I also tried with "GUEST": {"disabled": true}, no luck

My problem is that if I do this

pullReplication.setAuthenticator(facebookAuthenticator);
pushReplication.setAuthenticator(facebookAuthenticator);

Nothing will ever get replicated/pulled from the server. However if I don't set an authenticator, everything is pulled. Is it something I am doing wrong? I really need to use the authenticator in order to prevent some documents to not being replicated for non-authenticated users.

Note! The token is good, as if I am looking in the users section of sync gateway admin, I can see the right profile id of the logged in user token I passed to the couchbase facebook authenticator.


Solution

  • In the Sync Gateway config you provided, the Sync Function is function(doc, oldDoc) {channel(doc.channels);} which means that if the document processed by Sync Gateway contains a string(s) under the channels field, the document will be mapped to this/these channel(s). Let's assume the following config file:

    {
      "log": ["CRUD"],
      "databases": {
        "db": {
          "server": "walrus:",
          "users": {
            "GUEST": {"disabled": false, "admin_channels": ["*"]}
          },
          "sync": `
            function sync(doc, oldDoc) {
              channel(doc.channels);
            }
          `
        }
      }
    }
    

    If the channels field doesn't exist then the document will be mapped to a channel called undefined. But the GUEST account has access to the * channel (a placeholder to represent all channels). So, all unauthenticated replications will pull all documents. Let's now introduce the facebook login field in the config file. This time, replications authenticated with a facebook token represent a new user which has only access to the ! channel by default (watch this screencast to understand the ! channel, a.k.a the public channel https://www.youtube.com/watch?v=DKmb5mj9pMI). To give a user access to other channels, you must use the access API call in the Sync Function (read more about all Sync Function API calls here).

    In the case of facebook authentication, the user's facebook ID is used to represent the user name. Supposing that the document has a property holding the user's facebook ID (user_id: FACEBOOK_ID), you can map the document to a channel and give the user access to it. The new Sync Function would look like this:

    function(doc, oldDoc) {
      channel(doc._id);
      access(doc.user_id, doc._id);
    }
    

    You can retrieve the user's facebook ID with the Facebook Android SDK and save on a document field.