"403 missing role" error

I am trying to get authentication working with push replication. And I am getting a 403 missing role error when I try to push. Platform is iOS. Using Couchbase Lite 1.2

Here is my sync gateway config…

{
  "log": ["*"],
  "databases": {
    "db": {
      "server": "http://$COUCHBASE_CLUSTER_IP:8091",
      "bucket": "sync_gateway",
      "users": { 
      	"GUEST": {
      		"disabled": false, 
      		"admin_channels": ["object_library"]
      	},
      	"david": {
          "admin_roles": ["object_library_admin"],
          "password": "foo"
        }
      },
      "roles": {
      	"object_library_admin": {
      		"admin_channels": ["object_library"] 
      	}
      },
      "sync": `
function(doc, oldDoc) {
	requireRole("object_library_admin");
	if (doc._deleted) {//deleted documents don't have a type any more
			return;
		}
	if (doc.type == "library_object" || doc.type == "library_package") {
		channel("object_library");
	} else {
		throw({forbidden: "Invalid document type"});
	}
}
    `
    }
  }
}

Client side this is what I am calling…

NSURL *syncURL = [[NSURL alloc] initWithString:kSyncGatewayUrl];    
_push = [_database createPushReplication:syncURL];
    
id<CBLAuthenticator> authenticator = [CBLAuthenticator basicAuthenticatorWithName:@"david" password:@"foo"];
_push.authenticator = authenticator;    
_push.continuous = NO;

This is the sync gateway log…

2016-04-28T18:03:32.826Z HTTP:  #003: POST /db/_revs_diff
2016-04-28T18:03:32.841Z HTTP+: #003:     --> 200   (15.5 ms)
2016-04-28T18:03:32.892Z HTTP:  #004: PUT /db/D165C20D-D359-4C19-A83A-CB124E66BF22?new_edits=false
2016-04-28T18:03:32.943Z Attach: 	Added attachment "sha1-nvWSiP0dOFl6KNd+mO5KZkLa/fY="
2016-04-28T18:03:32.945Z Attach: 	Added attachment "sha1-rhaI5QtoF8MmlCM61HxwaofRCy0="
2016-04-28T18:03:32.945Z CRUD+: Invoking sync on doc "D165C20D-D359-4C19-A83A-CB124E66BF22" rev 3-d4f29c6cd3c58d013cdc5702269d4918
2016-04-28T18:03:32.946Z CRUD+: No old revision "D165C20D-D359-4C19-A83A-CB124E66BF22" / "2-315aa9e94e8bb3119dcef8b6930cb144"
2016-04-28T18:03:32.947Z CRUD+: No old revision "D165C20D-D359-4C19-A83A-CB124E66BF22" / "1-d664caca5872671a6fd967c60575a9e2"
2016-04-28T18:03:33.044Z Sync fn rejected: new=map[package:pkg_C74B72E2-1246-427D-AA28-61399E743C6E type:library_object _attachments:map[object:map[stub:true digest:sha1-nvWSiP0dOFl6KNd+mO5KZkLa/fY= revpos:3 content_type:application/octet-stream length:2338] thumnbail:map[stub:true digest:sha1-rhaI5QtoF8MmlCM61HxwaofRCy0= revpos:3 content_type:image/png length:4131]] _id:D165C20D-D359-4C19-A83A-CB124E66BF22 _rev:3-d4f29c6cd3c58d013cdc5702269d4918 _revisions:map[ids:[d4f29c6cd3c58d013cdc5702269d4918 315aa9e94e8bb3119dcef8b6930cb144 d664caca5872671a6fd967c60575a9e2] start:3] name:{"en":"Pencil"} order:0]  old= --> 403 missing role
2016-04-28T18:03:33.046Z HTTP: #004:     --> 403 missing role  (154.1 ms)

And on iOS I see this error:

2016-04-28 11:03:54.418 Concepts[8058:10662381] Error Domain=CBLHTTP Code=403 "403 forbidden" UserInfo={NSURL=http://redacted:4984/db/D165C20D-D359-4C19-A83A-CB124E66BF22?new_edits=false, NSLocalizedFailureReason=forbidden, NSLocalizedDescription=403 forbidden}

I would appreciate any advice on what I am doing wrong. Thanks!

From the Sync Gateway logs, it looks like the request is coming through as GUEST, and not as user david - if the request was being executed as david, I’d expect the log message to look like:
HTTP: #004: PUT /db/D165C20D-D359-4C19-A83A-CB124E66BF22?new_edits=false (as david)

Since GUEST doesn’t have the object_library_admin role, the sync function is rejecting the write.

@jens - any idea what’s missing in their client code to set the user credentials properly?

In 1.2 we weren’t sending the HTTP Basic auth credentials until we got a 401 challenge from a server response. (*) That’s fairly typical of what a browser does, but it means that if the server has guest access enabled, it never returns a 401 so the client won’t send the credentials.

This is fixed in the 1.2.1 release, which is in final testing and should be out very soon.

Until then you can work around the problem by removing the GUEST user from the SG config file.

Great to know, thanks! I can’t remove GUEST as we want anyone to be able to read, but want to restrict write. Or is there a better way to do that? I can afford to wait a couple of weeks on this so, if the answer is to wait, that’s fine too.

On a related question, when the push was failing, the attachments on the document were still getting pushed. It seems that the sync function does not get applied to validating security on pushing attachments?

It looks like the solution to both issues is to create an account that is used for authentication in the case the user is not logged in through another one. So an equivalent of guest, embedding the password in the app is not an issue as we consider the channel public. In this case attachments do not get uploaded when attempting to write when not authorized. But given the changes you are making for 1.2.1 to make guest work for this type of use case, it does seem there is a hole in security that attachments get pushed before the document write permissions have been determined.