Couchbase Lite Sync with authentication


#1

Hi,

I have set up GrocerySync Android app, modified it with few aditional data fields, added manual conflict resolution and editing options.

I also set up Sync Gateway, first with built-in walrus database and everything was working fine. Then I configured Couchbase Server, created new bucket and changed config.json for Sync Gateway to point to that server/bucket. Still working as expected.

Then I wanted to restrict access to user. I created user via REST PUT localhost:4985/grocery-user/_user/user1 with json

{  
   "name":"user1",
   "password":"some-pass",
   "email":"some-email"
}

Logs looks ok, so I added

pullReplication.setAuthenticator(new BasicAuthenticator(USER, PASS));
pushReplication.setAuthenticator(new BasicAuthenticator(USER, PASS));

(If I do not add authenticator, I get 401 Unauthorized from Sync Gateway console)

This is where it stops syncing. I am running two emulators side by side. If I add one item on the first emulator, it will not sync to the other. It will also not sync to the same emulator, if I clear app data. If I log into couchbase console, I see data from both emulators.

this is current config for sync gateway:

{  
 "log":[  
     "CRUD+",
     "REST+",
     "Changes+",
     "Attach+"
  ],
  "databases":{  
     "grocery-user":{  
     "server":"http://localhost:8091",
     "bucket":"grocery-user",
     "username":"grocery-user",
     "password":"pass",
     "sync":`   
function(doc){   
     channel(doc.channels);   
    }`
   }
  }
}

basically, I removed guest option.

Here is Sync Gateway log if I add one item:

HTTP:  #001: POST /grocery-user/_changes  (as user1)
Changes: MultiChangesFeed({*}, {Since:1 Limit:50 Conflicts:true IncludeDocs:false Wait:true Continuous:false Terminator:0xc21011bd80}) ...   (to user1)
Changes+: MultiChangesFeed: channels expand to channels.TimedSet{"!":0x1} ...   (to user1)
HTTP:  #002: POST /grocery-user/_changes  (as user1)
Changes: MultiChangesFeed({*}, {Since:1 Limit:50 Conflicts:true IncludeDocs:false Wait:true Continuous:false Terminator:0xc2101d5e40}) ...   (to user1)
Changes+: MultiChangesFeed: channels expand to channels.TimedSet{"!":0x1} ...   (to user1)
Changes+: MultiChangesFeed waiting...   (to user1)
Changes+: MultiChangesFeed waiting...   (to user1)
Changes+: Waiting for "grocery-user"'s count to pass 0
Changes+: Waiting for "grocery-user"'s count to pass 0
HTTP:  #003: POST /grocery-user/_revs_diff  (as user1)
HTTP:  #004: POST /grocery-user/_bulk_docs  (as user1)
CRUD+: Invoking sync on doc "9cd81cfb-ec67-4b6a-a7e9-a1cb1fa2c57d" rev 1-2ee478d5c9bb7cf3cb6f156a8b89bc4c
CRUD: 	Doc "9cd81cfb-ec67-4b6a-a7e9-a1cb1fa2c57d" in channels "{demo_channel}"
CRUD: Stored doc "9cd81cfb-ec67-4b6a-a7e9-a1cb1fa2c57d" / "1-2ee478d5c9bb7cf3cb6f156a8b89bc4c"
HTTP:  #005: PUT /grocery-user/_local/9fb3c812c5ba877f8c73e6b95b5ded5cd0760053  (as user1)
Changes+: Notifying that "grocery-user" changed (keys="{*, demo_channel}") count=2
Changes+: MultiChangesFeed: channels expand to channels.TimedSet{"!":0x1} ...   (to user1)
Changes+: MultiChangesFeed: channels expand to channels.TimedSet{"!":0x1} ...   (to user1)
Changes+: MultiChangesFeed waiting...   (to user1)
Changes+: MultiChangesFeed waiting...   (to user1)

What am I missing? does sync function for Sync Gateway needs to be properly rewritten?
I want per-user grocery items.

Thanks a lot for pointing me in the right direction :wink:


#2

Hello @TurboRudi,

Yes. The default sync function doesn’t give the user access to the documents. So when starting the pull replication, Sync Gateway doesn’t know the grocery documents that belong to this user.

To have per-user grocery items synced, you could have the following sync function:

function(doc, oldDoc) {
  channel(doc.channels);
  access(doc.username, doc.channels);
}

Then, in the grocery documents, add the channels property, it can hold an array of channel names and the username property (in this case user1). The pull replication should then sync those documents.

James


#3

@jamiltz thanks for reply! It works now.

Now I also create channel with the same name as username for each user (in this case user1) and set document’s channels to user1 when saving a document. If I want to share document to group, I also add group’s channel name to document’s channels. Is this good practice or is there better/recommended way?

Thanks again,
TurboRudi


#4

Yes. There’s no limit on how many channels you can create. And the same document can belong to many channels.

For sharing documents between users, you can:

  • have one channel per document and one for grouping the documents. Then giving the user access to that group channel as you described
  • use the existing one channel for each document approach and give the user access to each channel

Both options are valid. I think which one to choose depends on the use case. For example, the team at Spraed used the one document per channel approach to give users access to Spraeds. The slides explain this better (from slide 32 onwards).

James