Skip to content
This repository has been archived by the owner on Apr 12, 2022. It is now read-only.

Swagger-client using cached credentials/cookie on iOS? #89

Closed
Prudwell opened this issue Sep 20, 2016 · 9 comments
Closed

Swagger-client using cached credentials/cookie on iOS? #89

Prudwell opened this issue Sep 20, 2016 · 9 comments

Comments

@Prudwell
Copy link

Not really sure where to put this but since I think @jamiltz mentioned that this package was going to be using swagger spec in the future that I'd put it here.

So I'm trying swagger-client in my iOS react native app to make a request to the public sync gateway session endpoint, sending a username and password. I've been using a handmade fetch query till now but was having an issue with an unfulfilled promise when sync gateway sends a 401 that was hanging my app.

So with swagger, my issue is that the request seems to be using a cached cookie or something if there's a previous successful login/replication process. I am not manually sending any cookie in this request and I do not have enableCookies: true in my swagger-client constructor (and i'm not sure that would do anything in an ios environment anyways). So when I send the request

var publicClient = new Swagger({
    url: 'http://developer.couchbase.com/mobile/swagger/sync-gateway-public/spec.json',
    usePromise: true,
})
    .then(function (res) {
        res.setHost('192.168.99.100:4984')
        publicClient = res;
    });

publicClient.session.post_db_session({db: 'consumer_app', SessionBody: {name: ident.body, password: credentials.password}})
.then((res)=> {
    //do stuff
}

I see in my sync gateway logs something like

2016-09-20T22:43:35.895Z HTTP:  #164: POST /consumer_app/_session  (as 9490dd12-debd-3081-2367-84e0dbfa001f)
2016-09-20T22:43:36.006Z HTTP+: #164:     --> 200   (114.2 ms)

Wat. Posting to session AS a user?

It returns 200 regardless of what password I send. I'm guess this is a session refreshing mechanism for logged in users? I don't know why sync gateway sees that request as coming from a specific user.

I haven't been able to get a look at the exact http requests going across the wire yet, but I'm wondering if iOS is cacheing cookies that the previous replication process was using and sending them along in this request?

This comment got me thinking about this

couchbase/sync_gateway#758 (comment)

For some reason I didn't have this issue when using fetch api directly.

It's very possible I'm making a silly mistake somewhere. Has anyone run into something similar before?

@jamesnocentini
Copy link
Contributor

jamesnocentini commented Sep 21, 2016

It would help to know the exact headers and body sent in the request. Since you're sending the request to Sync Gateway you could use a tool like httpscoop or Charles to inspect the web traffic when running the react native app on the simulator. I've seen a caching issue once in an <Image /> component where it would load the same image even when changing the url. I didn't figure out the root cause.

@Prudwell
Copy link
Author

Here's the raw dump from Charles (super cool program btw thanks for clueing me in).

POST /consumer_app/_session HTTP/1.1
Host: 192.168.99.100:4984
Accept: application/json
Content-Type: application/json
Connection: keep-alive
Cookie: SyncGatewaySession=672a87eca1c6d9c64ce94ded6ba2a80f8a301d55
Accept-Language: en-us
Content-Length: 64
Accept-Encoding: gzip, deflate
User-Agent: prudwell_app/1 CFNetwork/808.0.2 Darwin/15.6.0

{"name":"9490dd12-debd-3081-2367-84e0dbfa001f","password":"pas"}

It's indeed sending along a session cookie but I don't know how it gets there. This password 'pas' is incorrect but the request returns 200.

@Prudwell
Copy link
Author

After some research about how iOS handles cookies, I decided to try deleting them on the iOS side before sending out the swagger-client request. Using the package react-native-cookies I can wrap my login code with CookieManager.clearAll((err, res) => { and now the requests are working as expected, no old cookie. I'm assuming the cookie is 'set' in iOS when I start the replication process using a session cookie.

Is this how it's supposed to be? I've also noticed sometimes my sync gateway getting other requests with a user context when I expected the the request to be from a 'logged out' context.

Is there a way to handle this cookie stuff in package or is there some standard action I'm supposed to be taking to ensure that there's no old user credentials being used for http requests? My usage of react-native-cookies feels kind of hacky and I think I'd need to use it in a lot of places to make sure the requests are 'fresh' when I want them to be.

@Prudwell
Copy link
Author

Have been doing a bit more digging this afternoon. Now my feeling is that the cookie is not set by CBL, well not on a scope that swagger-client would see.

Maybe the Set-Cookie header in the response from /session is actually setting the cookie like it would in a browser? I've been manually plucking the SyncGatewaySession cookie out of that response to plug it in to the CouchBase Lite replicator function but maybe it's also getting set on a more global scope. I can't really find out how react native works and ties into iOS's cookie storage yet. Will keep looking

@Prudwell
Copy link
Author

Ok yes it looks like react-native does have some automatic handling of cookies with fetch requests.

So I guess doing custom authentication on react-native just has this effect if you are directly making a call to the public API endpoint which returns a Set-Cookie header on success. I didn't intend to use that Set-Cookie header in this way; I just wanted to pull out the SyncGatewaySession to use in replication.

As an aside, It's odd that the /session endpoint treats the Cookie as the authentication even when credentials are sent.

Going to keep this issue open for now in case anyone has any further insight, but my assumption at this time is that I'll need to keep using react-native-cookies to make sure I'm sending requests without user context if I want to keep using the public session endpoint directly from my react-native app.

If I don't discover anything new I'll close in a few days.

Cheers

@npomfret
Copy link
Contributor

There's an alternative to using a session for the replication, you can just pass through an Authorization header.

@Prudwell
Copy link
Author

@npomfret Interesting. Could you point me in the right direction to learn more about that?

Thanks!

@npomfret
Copy link
Contributor

    import base64 from "base-64";
    ...
    const authToken = "Basic " + base64.encode(userId + ":" + password);
    const source = 'databaseName';
    const target = {headers: {Authorization: authToken}, url: url};

    return this.cbl.replicate(source, target, options)

@Prudwell
Copy link
Author

Prudwell commented Oct 3, 2016

Cool thanks for the explanation! I don't think I've actually seen that method mentioned in the docs but I definitely could have just missed it. I tried it out and it worked like a charm. Not sure if I'll use this or the session yet, it's great to have options.

Cheers

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants