3

I have a React application for my business that has multiple clients. My architecture is set-up like this:

Server A - Main Application Server, only serves React app (url = https://example.net)

Server B - Business Client #1, serves their slightly customized React app (url = https://example2.net)

Server C - Business Client #2, serves their slightly customized React app (url = https://example3.net)

Microservice Server - Running a Java microservice (url = https://api.example.net - notice it's a subdomain of Server A)

Server A, B, and C are all talking to the Microservice Server for API calls. The API calls are using the native browser fetch() call with these options

var options = {
   method: "{method}",
   mode: "cors",
   credentials: "include",
   headers: { "Content-Type": "application/json" }
}

The server response has this header:

"Access-Control-Allow-Credentials", "true"

and uses the microservice's built-in

enableCorsForAllOrigins()

Now, this all works fine on Firefox, Chrome, IE, and Edge. However, it doesn't work on Safari (MacOS or iPhone) for Server B or Server C. It works fine on Safari on Server A.

The API calls are in this sequence...

POST https://api.example.net/login

GET https://api.example.net/users

On Safari, the first call is successful, but the second call fails with a 401 error. The 401 error is thrown from the microservice trying to get the Session attribute for the "currentUserId", which is null. In other words, Safari is not sending the cookie to the server. I've figured out that Safari doesn't like that the API calls are going to a different URL base than the client's server. (In other words, Server B making API calls to Server A).

To further verify this, if a user starts on Server B, then navigates to Server A and makes an API call (a bad login for example), and then goes back to Server B, then it works for them on Safari. It seems like Safari just needs to exchange a cookie on the Server A URL to get it working on every other server.

Now, my issue...how do I fix this? Users on Mac Safari and iPhone Safari on Server B and Server C are not able to use the application because their session cookies are not sent to the server. Has anyone seen this before? Does anyone have solutions? My first attempt was to create an iframe on Server B that loaded Server A's page, but that didn't work.

Many thanks in advance!

bluedevil2k
  • 9,366
  • 8
  • 43
  • 57
  • Might be caused by ["sameSite" cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies) (and a related [bug in Safari](https://stackoverflow.com/questions/58525719/safari-not-sending-cookie-even-after-setting-samesite-none-secure)). – str Jan 29 '20 at 16:10

1 Answers1

1

So after reading the links provided by @str above, and reading the links from those links, I came to the conclusion that trying to get it working using cookies and sessions wouldn't work with Safari. So I scrapped the entire setup and went with JWT instead.

So, my solution now is:

POST https://api.example.net/login -> Response includes server-generated JWT token with userId in it.

JavaScript code now saves this JWT into SessionStorage (not a cookie, or you'll run into the same issue as when you used Sessions).

window.sessionStorage.setItem("jwtToken", data.jwtToken);

Set up the API calls to always pass the JWT token as a header.

"Authorization": "Bearer " + window.sessionStorage.getItem("jwtToken")

Then, using a server-side library, I can extract the JWT token and get the userId from it.

It works on Safari and is the preferred solution nowadays for web authorization, so I'd say go with this solution. The downsides is that storing the JWT in sessionStorage opens the user to XSS attacks.

bluedevil2k
  • 9,366
  • 8
  • 43
  • 57
  • "[...] trying to get it working using cookies and sessions wouldn't work with Safari" – Well that is not true. Of course cookies work with Safari. But depending on your setup and the versions of Safari that you tested, you have to adapt a little. – str Jan 30 '20 at 17:05
  • "it" = cross-site API calls – bluedevil2k Feb 05 '20 at 21:07