Safari isn't sending XMLHTTPRequest cookies at random - express

I have an Express server using PassportJS authentication. I am using standard XMLHTTPRequests to retrieve files.
Chrome and Firefox work without problems. Safari (Mac and iOS) is failing to send a request cookie with some requests and doing this inconsistently.
Logged failed requests all have a different session id that isn't authenticated with PassportJS.
Chrome and Firefox have a single session id for all requests.
I'm not using cross-origin requests, these are all same-origin. I have tried using withCredentials true with the same result but this property shouldn't be needed anyway as it's same-origin and Chrome and Firefox work without it.
There doesn't seem to be a pattern to which files fail to send the request cookie. Sometimes at random (but rarely), all requests load ok.
It's not a sequential failure, most requests succeed to begin with then a sequence of fails with some successes in between.
The Express server isn't destroying the sessions, they keep accumulating. Failed requests have session ids that are not authenticated sessions.
What would cause Safari to randomly not send the request cookie for some XMLHTTPRequests?

It turned out to be the crossOrigin property, I must have been doing cross-origin requests:
https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes
Most of my loaded files had their crossOrigin property set as "anonymous". For some reason, Chrome and Firefox seemed to ignore this and sent the cookie credentials anyway but Safari didn't. Setting this property to "use-credentials" worked fine:
img.crossOrigin = "use-credentials";

Related

CSRF and CORS: Why allow the request to happen if we know there will be a cors error?

I am confused by why the cors package allows the request to be processed even if the origin in the request header isn't white-listed. For example, res.status(202).send(await User.find()) returns a response with status code 202, but the data can't be loaded in the Chrome console.
Also, doesn't the browser send preflight OPTIONS requests to know what's allowed; why would it send cookies/credentials along a request with a disallowed origin?
Edit: Tried a post request on jsfiddle and the post request doesn't happen server side. When I said "why the cors package allows" it would be better to say why the browser allows.
CORS is enforced in the browser, not in your server. The server participates in setting headers that the browser can then use to determine whether the request should be allowed or not. But, it is the browser that ultimately decides whether the CORS request satisfies the requirements or not and the result should be passed through to the Javascript in the browser.
Thus, the request is sent to the server, response is received and THEN the browser decides whether the Javascript in the page is allowed to see the result or not.
In some cases where the request is likely to have side effects on the server (based on a set of criteria in the request), the browser will send a pre-flight request to get just the CORS info first.

Is there a way for a SPA to check if there's a proxy and handling it properly?

We have developped a SPA SaaS and went to a soft production launch recently.
Everything was fine until one of our customers told us they had trouble using the app.
Once they open the app, the first request to our backend triggers their proxy credential prompt. Hopefully on the login request.
They have to enter their proxy credentials to let the request go. All subsequent requests are passing properly and they can use the app.
The problem is:
When they stop using the app, close the browser and then come back the day after, the persistent login tries to connect them to our backend, but the proxy credentials prompt is not triggered and the request fails. All subsquent requests fail also.
For it work again, they have to delete all app data in chrome (so the service worker is unregistered, the localstorage and cache are cleared). The next api call will trigger their proxy credentials prompt and they will be able to work again.
So is there any way for the app to know if the proxy is set or not ? Any way of triggering the proxy prompt if not set or whatever ?
I don't exactly know how those proxies work and we have zero access to the proxy settings.
It surely is something with the credentials expiration after some time but that's all we can figure out right now. Maybe we could monitor some params in the request headers ?
We are using VueJS with axios for the requests.
My guess is when user session credentials get expired, your UI is not handling redirection to login page. When the user login for the first time you should store that the user has logged in successfully in browser localstorage. If your server returns 401 error code, you can delete the flag and redirect the user to login page. You can achieve that using meta fields in router.
Check out this link on how to use meta fields https://router.vuejs.org/guide/advanced/meta.html

Create session via API Token sets an expired cookie

I am trying to create a session via an API token .
The request is sent successfully, I'm getting 3 cookies in the response, but their expiry date is the same as the time at which the request was sent, which invalidates them instantly.
For example, if the request is sent successfully on 20 April 2020, 00:24:29 this is the response:
Set-Cookie: persistent=XXXX; path=/; expires=Thu, 19-Mar-2020 00:24:30 GMT; secure; HttpOnly
Set-Cookie: onelogin.com_user=XXX; domain=.onelogin.com; path=/; expires=Mon, 20-Apr-2020 00:24:30 GMT; secure; HttpOnly
Set-Cookie: sub_session_onelogin.com=XXXXXXX; path=/; secure; HttpOnly
The second cookie that creates the session for using it on Onelogin is already expired.
Is there anything that is being done wrong? Any help would be much appreciated.
I am using HTTPS.
The initial API request to get the session token (note, not cookie) is a backend call from the app to the OneLogin API and so it is not visible in user browser, but you can see that that API call returns the JWT token with a 2 minute timeout to a customer app. You can navigate to https://jwt.io/ and click Debugger for verification. There will be an expiration area on the right side and you can hover over this to see the expiration. There is always a 2 minute timeout associated with it.
It is then the responsibility of your app to direct the user's browser to send the token in a HTTP payload to OneLogin at "/session_via_api_token", so as to provide a session cookie to the browser. You can use the SAML Tracer extension in your browser to capture a SAML trace to verify, by viewing the "HTTP" tab of the POST line for "session_via_api_token". Note: 3 cookies are set, of which 2 are persistent and are immediately set to expire - so they serve no purpose. So don't stress the fact that those 2 cookies are expired as it's not relevant. The key cookie is "subsession_onelogin.com" which is a session cookie and so has no expiry time in it. It is used to track session timeout.
If the user then attempts to get access to a OneLogin resource then it includes the new "subsession_onelogin.com" cookie and so is considered authenticated. OneLogin then issues an updated version of that cookie, which you can see in the SAML Trace on the GET line for the "sub_session.onelogin.com" Set-Cookie area.
It appears that the way the CORS method works for this API is slightly different than the form post method, and the API team has been made aware, as the Dev article will likely be updated to state that the form post method is suggested.
In addition, per my testing it appears that some browsers react differently, so testing other browsers would certainly shed more light. That is browser specific and as such wouldn't be related to the API calls. With the Create Session request on Safari when the default setting of "Prevent Cross Site Cookie Tracking" is enabled I have noticed that the cookies are returned as expected but the browser refuses to accept them. There is no warning or error in the browser console. It's unknown why Safari does this and can't find any documentation about it so that's a browser specific issue out of our control.
Browsers are making it harder so our API team will probably update our best practice guidelines in the coming months to say to use the form post redirect rather than CORS. The form post method should work for any and all browsers as they handle it different than the CORS method.
The first link in the API docs is https://developers.onelogin.com/api-docs/1/login-page/create-session-via-token and the second link to use it https://developers.onelogin.com/api-docs/1/login-page/create-session-login-token. You can then use https://jwt.io/ and click Debugger for verification. It's working properly with a 2 minute expiry which is correct. The key cookie is "subsession_onelogin.com" which is a session cookie and so has no expiry time in it as it is used to track session timeout.
In my tests I have verified that there is definitely a 2-minute lifetime, which is exactly how it should work. In my example tests it shows that the API response is claiming expiry at 16:19:04, which matches the jwt.io decoder (that site is useful for this). The 2nd part of the test returned the headers DATE parameter which shows the time of issue was 16:17:04; i.e. a 2 minutes before expiry. This is correct.
I imagine the problem you are having is caused by how your app directs the user's browser to invoke the "/session_via_api_token" call - if the app uses CORS then obviously you would need to include the relevant header. However, it appears that even with that header, some browsers will not send the CORS request (presumably due to browser security settings) and so the session never gets established. At least that is what I can see when using the sample custom login page in testing. Note: The first call is a REST API call (i.e. what Postman is good for), but the 2nd request is a regular browser request.

How do I automate cookies synchronization between postman and the browser

When using the Postman Chrome App with the Interceptor extension it's easy to reuse the browser's cookies in order to log into an app and then call the services within.
Since moving to the Postman standalone app, this process has become somewhat manual. After logging in from the browser, I have to access the JSESSIONID cookie in the developer tools and copy its value over to postman.
When my session expires I need to repeat the process.
I would like to automate this synchronization or at least understand how I could obtain the new authenticated value in postman. It's important to note that none of the authentication mechanisms available in Postman work with my app which is why the manual login in the browser is necessary.
You can get JSESSIONID cookie in Postman Standalone in similar way your browser do it - by send proper requests (probably POST "login" request with user credentials) to server

Rails 3 and encrypted cookies with IE9

I am using encrypted cookies to pass data to my rails app. Once these cookies get set, they are being passed just fine to my app from Chrome, Firefox, Safari (iOS and OSX version), etc.
However, the cookie isn't being passed back when I do the same on Win7/IE9.
I'm using Ruby 1.9 and Rails 3, and using the cookies.permanent.encrypted method of setting the cookie. E.g:
cookies.permanent.encrypted[:some_data] = object.some_data
Any ideas about what's going on?
It turns out it has to do with my P3P policy not getting passed correctly prior to the cookies being set. IE prevents cookies if that's the case, other browsers apparently don't care about that.