Cookie-based authentication for iOS for Ionic React / Capacitor

Hello all,

This question is about making cookie-based authentication work for our Ionic app running on iOS native platforms. I’ve searched and read several forum posts on this topic and tried the suggestions provided in there but none seem to work. Below is a summary of our setup and the things we tried to solve it. Any help, insights, etc. is highly appreciated! I apologize beforehand if I missed a solution from another forum post :slight_smile:

We are using Ionic React 5.2.3 with Capacitor 2.2.1 to build an app for the following target platforms: Web, iOS native and Android native.

The app gets the data from our API backend that we invoke using Axios. The backend requires authentication based on server-side cookies. After successful login, the server returns a SetCookie header in the response with the client’s session id. This single cookie is stored in the client and in subsequent calls to the backend the cookie with the client’s session id is passed. The backend retrieves the associated client data from a session store based on the session id, and can verify the user is authenticated.

  • For Web this is working ok. The SetCookie directive is configured as HttpOnly, Secure and SameSite=strict since the client origin has the same domain.
  • For Android native, the origin of the client is http://localhost (see https://ionicframework.com/docs/troubleshooting/cors). After setting SameSite on the backend to None (while we keep Secure and HttpOnly), all still works. The SetCookie directive in the responsive of a successful login is processed and subsequent calls to the backend include the cookie with the session id.
  • For iOS native, this approach doesn’t seem to work since WKWebView ignores the SetCookie directive: the cookie isn’t stored and subsequent calls to the backend APIs won’t include the cookie with the session id causing the calls to fail.

I’ve read various forum posts and tried the suggestions/solutions provided there, although none seem to make it work on iOS native:

  • Setting the server.host parameter in capacitor.config.json to the same domain as the backend API so the cookie isn’t regarded as 3rd party cookie and SameSite can be set to strict. We rather don’t use this approach due to the drawbacks mentioned in https://capacitorjs.com/docs/reference/config. Also, when setting this parameter to an external URL (i.e. same domain as the domain on which the backend API runs), after opening the app, iOS will automatically open a browser window and browse to that URL instead of just opening the app.
  • Using the plugin for native HTTP (https://github.com/capacitor-community/http). This plugin is mentioned in various threads to prevent cross-site issues from native apps by using native HTTP calls instead of executing HTTP calls from the WKWebView layer. This plugin also has some APIs to manage cookies. When we swap Axios for this library, we can successfully execute HTTPS calls to the backend. However, the SetCookies directive in the response of a successful login call is ignored, and no cookie (with the session id) is stored in the client. For Axios, we include the withCredentials directive. It looks like there is no such option in the native HTTP library? I can use the various available cookie APIs from the plugin (e.g. setCookie, getCookie) but those seem to be for managing client-side cookies, not for making the SetCookie directive to store a cookie from the response.
  • Configuring WKWebView to accept SetCookie directives (with Secure, HttpOnly and SameSite set to none). I haven’t delved into this option very deeply, but what I read in various posts is that this depends very much on the iOS version whether this is even possible. So it doesn’t seem to be a viable way.

We want to go to the Appstore as soon as possible and this completely blocking our progress. Any help is greatly appreciated.

Ronald

2 Likes

A quick update from my side.

I was under the impression that the plugin for native HTTP (https://github.com/capacitor-community/http) didn’t achieve the result I was looking for since the Developer Tools in Safari didn’t show any cookie set. However it turns out that when using the native HTTP plugin, the SetCookie directive causes the cookie to be created and stored, and sent by iOS in subsequent requests to the backend. So using the native HTTP plugin solves the issue we ran into.

We ran into some other issues when trying to use the HTTP plugin for web (non-native) calls, e.g. when trying to pass some security related info to the backend. What we do now is to use Axios for non-native HTTP calls and the HTTP plugin for the same backend calls, but then from native iOS platforms.

Ronald

4 Likes

Glad you were able to figure it out. We are going to be putting more work into that plugin so please let us know where we can improve by filing an issue. Tell them I sent you :slight_smile:

1 Like

We used the same solution and it works :ok_hand:

1 Like

Hi, I am stuck on the same thing, can you please point out, how you used the setCookie directive to set the session cookie? Thanks a lot

Hi, I am not sure how to get the session cookie from my response from using the set/getCookie, can you point out on how to do that? I guess it would also be a nice example for the documentation. Thanks

Hi,

In the end we didn’t explicitly use the set/getCookie methods from our code.

We did something like the following:

if (isIosNative()) {
  result = await NativeHttp.post(backendUrl);
}
else {
  result = await Axios.post(backendUrl);
}

We use Axios for calling our backend. In case the app runs on iOS native, we invoke the backend using the Capacitor native HTTP plugin. The plugin itself handles the SetCookie directive returned from the backend response. In subsequent calls, when you use the Capacitor native HTTP plugin, the plugin will pass the cookie to the backend.

Hope this helps,

Ronald

1 Like

Thanks a lot, I will try it :slight_smile:

Hi,

@rluttikhuizen thanks for exposing your problem but i was wondering how do you manage CSRF Attacks? Since for Android you are using None for samesite cookie, you seem to be exposed to these kind of attacks. Did you solve this problem?

Also, don’t you have this problem https://github.com/ionic-team/capacitor/issues/3012
That the httpOnly Cookie for sessions are not persisted when the app is exited?

Thanks!

As an update to this issue. We had the same problems but we managed to solve it just using axios. I added to capacitor.config.json the following plugins:

{

  "plugins": {
    "CapacitorCookies": {
      "enabled": true
    },
    "CapacitorHttp": {
      "enabled": true
    }
  }
}

And then axios started to work properly :thinking: also I see the http plugin is depreacted. Did anyone solve the CSRF problem which @grandours35 was asking ? Basically I reached the same situation as him we have same-site set to none and this makes us vulnerable to CSRF

The community http plugin was deprecated because it is now 1st party here - Capacitor Http Plugin API | Capacitor Documentation

1 Like