ionic-team / capacitor

Build cross-platform Native Progressive Web Apps for iOS, Android, and the Web ⚡️
https://capacitorjs.com
MIT License
12.33k stars 1.01k forks source link

bug: 3rd party cookies not working on capacitor iOS #6302

Open gabides opened 1 year ago

gabides commented 1 year ago

Bug Report

Capacitor Version

Latest Dependencies:

  @capacitor/cli: 4.6.3
  @capacitor/core: 4.6.3
  @capacitor/android: 4.6.3
  @capacitor/ios: 4.6.3

Installed Dependencies:

  @capacitor/cli: 4.6.3
  @capacitor/core: 4.6.3
  @capacitor/android: 4.6.3
  @capacitor/ios: 4.6.3

Platform(s)

ios

Current Behavior

When setting CapacitorConfig.server.hostname to localhost in capacitor config (default value, recommended to keep it that way in the doc), 3rd party cookies are not working.

other settings:

The doc of capacitor cookies suggest that 3rd party cookies are supposed to work

https://capacitorjs.com/docs/apis/cookies#third-party-cookies-on-ios

Expected Behavior

3rd party cookies should be set and used correctly by the capacitor webview

Code Reproduction

hard to create a reproduction setup as it involves a backend service sending cookies to set

Webrow commented 1 year ago

@gabides When we ran into this problem ( Januari 2022, close to the release of ionic 6 ) This was still a big issue because iOS didnt allow saving the cookies from 3rd party cookies.

Multiple issues were on at that moment, (I am reacting cause I am also curious if this is done nowadays). One of the issues was that cookie setting was done async (talking about capacitor) and therefor could not be guaranteed to be ready at time of using the other requests. (This is all old information).

As changing the localhost was not recommended, we went ahead and did it anyway (I know there are things like geolocation and media functions that have issues with it, but we didn't use them. Our apps are being accepted and approved, sadly the capacitorconfig didnt use to have a seperate flag for ios hostname and android hostname (our issues were only present on ios). So if you go that road, keep in mind that changing the hostname of android will make it NAVIGATE to the hostname url. using the following code (depending on youyr buildsystem) you can make sure to remove the key: value pair from the capacitor config file after cap sync.

#!/bin/bash

set -xe

FILENAME='./android/app/src/main/assets/capacitor.config.json'

[ ! -f ${FILENAME} ] && echo "capacitor.config.json could not be found in ${FILENAME}" && exit 1;

jq 'del(.server)' $FILENAME > ${FILENAME}.tmp && mv ${FILENAME}.tmp ${FILENAME}

with "capacitor:sync:after": "scripts/capacitor-sync-after.sh", in your package.json.

Just sharing what we produced back then, wondering if it is not the case any more :)

RobSchilderr commented 1 year ago

@gabides When we ran into this problem ( Januari 2022, close to the release of ionic 6 ) This was still a big issue because iOS didnt allow saving the cookies from 3rd party cookies.

Multiple issues were on at that moment, (I am reacting cause I am also curious if this is done nowadays). One of the issues was that cookie setting was done async (talking about capacitor) and therefor could not be guaranteed to be ready at time of using the other requests. (This is all old information).

As changing the localhost was not recommended, we went ahead and did it anyway (I know there are things like geolocation and media functions that have issues with it, but we didn't use them. Our apps are being accepted and approved, sadly the capacitorconfig didnt use to have a seperate flag for ios hostname and android hostname (our issues were only present on ios). So if you go that road, keep in mind that changing the hostname of android will make it NAVIGATE to the hostname url. using the following code (depending on youyr buildsystem) you can make sure to remove the key: value pair from the capacitor config file after cap sync.

#!/bin/bash

set -xe

FILENAME='./android/app/src/main/assets/capacitor.config.json'

[ ! -f ${FILENAME} ] && echo "capacitor.config.json could not be found in ${FILENAME}" && exit 1;

jq 'del(.server)' $FILENAME > ${FILENAME}.tmp && mv ${FILENAME}.tmp ${FILENAME}

with "capacitor:sync:after": "scripts/capacitor-sync-after.sh", in your package.json.

Just sharing what we produced back then, wondering if it is not the case any more :)

You can also add this to your capacitor.config.ts:


export const FRONTEND_URL = !IS_IN_PRODUCTION_ENVIRONMENT
  ? 'http://localhost:3000'
  : 'https://produrl.com'

const config: CapacitorConfig = {
 appId: 'yourapp.mobile.app',
  appName: 'YourApp',
  server: {
    hostname: FRONTEND_URL.split('://')[1],
  },
}

if (process.argv[3] === 'android') {
  // for android we want to use the default hostname, but for iOS we have to use the custom hostname due to 3th party cookies issue in webkit
  delete config['server']
}

export default config

This way you have default hostname in iOS, but it deletes on Android. Do note that I heard that in the future Android also plans to put these same restrictions for cookies that Apple does.

Webrow commented 1 year ago

Thanks for that one, yeah back when I wrote that pipeline our configs where json based. We hope we migrated by the time android make up their minds ;) The hostname "fix" won't work for android (since android resolves the hostname url, and iOS doesn't) Itll try to approach "hostname"/vendor.js I tried using the Capacitor browser the other day again, but it doesn't have some features that we like (hiding url bars and stuff like that) so it seems you stay "in your app". I hope the capacitor browser gets some features in the future, but sometimes it seems that the ionic team got their hands full on all sides. On the other hand, I personally like the transparancy of "You are not in your own safe environment", sadly business-wise I am not always the one making the decisions.

kennardconsulting commented 1 year ago

I can confirm this is not working as advertised in Capacitor 5.0.4 and iOS 14.

Hacking capacitor.config.json does work:

{
   ...
  "server": {
    ...
    "hostname": "my-actual-domain.com"
  }
}

But this is not recommended. Leaving it as localhost and adding WKAppBoundDomains (as advised by https://capacitorjs.com/docs/apis/cookies) does not work.

I'm sure the above workarounds (hack a script to replace capacitor.config.json and/or use capacitor.config.ts so that you can update iOS but not Android) work. However here's another option in case both of those aren't suitable for you (they weren't for me):

import UIKit
import Capacitor

class MyViewController: CAPBridgeViewController {

    override func instanceDescriptor() -> InstanceDescriptor {

        let descriptor = super.instanceDescriptor()

        // Hack until https://github.com/ionic-team/capacitor/issues/6302

        descriptor.urlHostname = "my-actual-domain.com"
        return descriptor
    }
}

This can be applied as instructed here https://capacitorjs.com/docs/ios/viewcontroller

A real fix would be really appreciated!

kbiefang commented 1 year ago

We are facing the same problem. We use a Session-Cookie which gets issued by our backend running on a custom domain which should be appended on any subsequent requests. Our WebView is running on localhost and the option to change it to the backend URL is not feasible, because we are relying on other secure APIs (like UserMedia).

We use the following settings:

Third party cookies seems to not get appended, although the docs seem to imply that (though they are not very detailed)

The only thing that helps is adding the NSCrossWebsiteTrackingUsageDescription to the Info.plist and allowing tracking in the iOS settings for our app. But this is of course not a suitable production solution.

Is there any other way we can get our third party cookie appended?

Update: I just used the XCode Network Instrument to take a look at the requests and it seems like our Session-Cookie does get appended. Nonetheless, the image we embed from the server cannot be displayed in the WebView. It seems to be another problem then, but it certainly has to do with Cross-site-tracking because enabling this in the IOS settings fixes the problem.

aanders77 commented 9 months ago

We're facing the same issue. Any news on this? I guess switching from session cookies to tokenbased authentication will work, but that's not what we prefer to do.

rricamar commented 8 months ago

Hello, guys, same behavior here:

However, the 3rd party cookie is not being sent until server: { host: api.service.com } is set in capacitor.config.ts. According to the docs, this last step shouldn't be needed, right?

Are we missing something else? 🤔

jonatato commented 8 months ago

I can confirm this is not working as advertised in Capacitor 5.0.4 and iOS 14.

Hacking capacitor.config.json does work:

{
   ...
  "server": {
    ...
    "hostname": "my-actual-domain.com"
  }
}

But this is not recommended. Leaving it as localhost and adding WKAppBoundDomains (as advised by https://capacitorjs.com/docs/apis/cookies) does not work.

I'm sure the above workarounds (hack a script to replace capacitor.config.json and/or use capacitor.config.ts so that you can update iOS but not Android) work. However here's another option in case both of those aren't suitable for you (they weren't for me):

import UIKit
import Capacitor

class MyViewController: CAPBridgeViewController {

    override func instanceDescriptor() -> InstanceDescriptor {

        let descriptor = super.instanceDescriptor()

        // Hack until https://github.com/ionic-team/capacitor/issues/6302

        descriptor.urlHostname = "my-actual-domain.com"
        return descriptor
    }
}

This can be applied as instructed here https://capacitorjs.com/docs/ios/viewcontroller

A real fix would be really appreciated!

With your solution we keep getting capacitor://mydomain

Any update on this ?¿

UniWorkerPtyLtd commented 8 months ago

@jonatato don't know what to tell you. The above solution is still working for me on Capacitor 5.7.2. Perhaps your ViewController isn't hooked up correctly?

rricamar commented 8 months ago

Hello, guys, same behavior here:

  • CapacitorCookies enabled in capacitor.config.ts.
  • WKAppBoundDomains set in Info.plist with localhost and api.service.com.
  • limitsNavigationsToAppBoundDomains set to true also in capacitor.config.ts.

However, the 3rd party cookie is not being sent until server: { host: api.service.com } is set in capacitor.config.ts. According to the docs, this last step shouldn't be needed, right?

Are we missing something else? 🤔

Just as further information:

After testing, I can confirm that only the server: { host: "domain.com" } setting is working (and therefore needed).

Alternative configurations for enabling 3rd party cookies (WKAppBoundDomains) appear ineffective, as the WKWebView doesn't attach the specified authentication cookie when making HTTP requests to these domains.

As previously mentioned, the documentation suggests that enabling this feature should result in the attachment of third-party cookies 🤔

Let's see if somebody from the team can provide more information about this topic 🙏

abegehr commented 7 months ago

Running into the same issue. Setting WKAppBoundDomains doesn't seem to be enough to allow cookies from a third-party domain. I'm using an iframe (that points to a remote url) in a capacitor app to show some content that requires setting cookies and it fails even though I have added the domain to WKAppBoundDomains as per the docs.

Setting "server": { "hostname": "…" } actually seems to work! However, I don't understand what it does and why it would work. Also it takes only one value, so this will be a problem with multiple third-party cookie domains.

Webrow commented 7 months ago

Setting the hostname makes the app think its that site. Doing this makes the cookie not "thirdparty" anymore. Because the cookie was issued for api.example.com, and the hostname is also api.example.com. Keep in mind this only works for iOS, on android XHR calls will fail because it tries to resolve your requests against the device itself.

On Mon, 15 Apr 2024, 23:29 Anton Begehr, @.***> wrote:

Running into the same issue. Setting WKAppBoundDomains doesn't seem to be enough to allow cookies from a third-party domain. I'm using an iframe (that points to a remote url) in a capacitor app to show some content that requires setting cookies and it fails even though I have added the domain to WKAppBoundDomains as per the docs.

Setting "server": { "hostname": "…" } actually seems to work! However, I don't understand what it does and why it would work. Also it takes only one value, so this will be a problem with multiple third-party cookie domains.

— Reply to this email directly, view it on GitHub https://github.com/ionic-team/capacitor/issues/6302#issuecomment-2057841540, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAYH27JO2MFMDRVQRONB6GDY5RBCTAVCNFSM6AAAAAAU6I34S2VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDANJXHA2DCNJUGA . You are receiving this because you commented.Message ID: @.***>

abegehr commented 7 months ago

Thanks for explaining, @Webrow! I tried using server.hostname setting with live reload, however that didn't work again, probably since url will override hostname, I'd imagine. Would be great to get first-class support for third party cookies on iOS 🙏

Webrow commented 7 months ago

I never used my live reload on an iOS device, we usually develop web-based and then do the testing on the mobile devices. We don't set the server.hostname until the build, and remove it during the capacitor copy step for android builds (as they don't have the problem). Edit: Eventhough its a hard decision, and it pushes devs to use other authentication or communication paths, lets try to make the world a third party cookie free world. (I work in legacy, so it's hard to solve it for me as well, but I do support the decision).

abegehr commented 7 months ago

We're aiming to migrate our application to Capacitor view-by-view and using iframes to show distinct pages of the legacy app, so it's not really 3rd party cookies however they will always be handled as third party, as the app normally runs on localhost, so to the client they are 3rd party.

farukbigez commented 1 week ago

Do we have an update for this issue?