-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[IOS] Cookies problems in IOS #1373
Comments
Any information about how to reproduce the issue? |
It's very simple, you can try to open console and do: document.cookie = "username=John Doe"; Expected: |
Relevant PR where that was introduced apache/cordova-plugin-wkwebview-engine#27 |
thank you jcesarmobile. Why was it not introduced even in Capacitor? How I do you put it manually? |
I still get the same using WKWebView. On UIWebView this simple javascript works. Is |
Are there any updates or workarounds? Thx in advance. |
Hi, same problem here. |
Hi, We have the same problem. We are using a third party JS framework which is performing the cookie test as mentioned below. addTest("cookies", function() { As document.cookie is always returning "" . We are unable to use the third party JS framework. I have created a singleton instance of WKProcessPool process and I have set it to webViewConfiguration.processPool = WKProcessPool().This didn't solve my issue as document.cookie is returning "". Please let me know if there is any workaround for the problem. Many Thanks |
@shivatangirala were you ever able to find an answer for this? Currently experiencing the same problem |
Currently the WKWebView does not accept/store/send XHR cookies. I believe this is currently an unsolved issue over in Cordova land as well. |
I ported over my Cordova app which was using https://github.com/oracle/cordova-plugin-wkwebview-file-xhr and it works here in Capacitor as well. |
Is this tagged as low priority because it's out of scope for capacitor? This bug makes user sessions with cookies impossible, so the app I'm working on is unable to support persistent authentication, which is essentially the foundation of the whole app. |
The wkwebview file xhr plugin is what I’m using for the time being until some one figures this out. |
@alexsasharegan for now it seems like this approach is the easiest if your app is working against one host: ionic-team/cordova-plugin-ionic-webview#22 (comment). I also do not understand how this issue could possibly be low priority. Is it because wkwebview is outside the scope/control of Capacitor? This problem forces to to either adopt the solution linked above, limiting you to one XHR host, or re-architecting your app to use |
Hi all, will take a look at this as part of #2495 |
@mlynch, the original issue is about I tested in the past (and again today) and the common process pool doesn't fix the issue on Capacitor, nor setting the cookies in the native side, they get set, but still not accessible on
|
@jcesarmobile makes sense. This won't solve that problem but provides an easy way to get system wide cookies using the native Cookie management APIs which I think solves this |
Any solutions ? It is working in android without any issues. |
I have do have some ideas and code on how to fix this annoying issue. Android cookies will work just fine as long as you have your webview server running on iOS cookies however... Well, they are something special. Let's assume your main backend is running on First of all you will have to add the following to your "server": {
"hostname": "subdomain.example.com"
} mind you that this config should only be applied for ios Then you should create a file which will handle the cookies for you. Let's call it import '@capacitor-community/http';
import { Capacitor, Plugins } from '@capacitor/core';
const url = () => {
return window.location.origin;
};
export const setCookie = (key, value) => {
const { Http, CookieManagerPlugin } = Plugins;
try {
const parsedValue = JSON.stringify(value);
value = parsedValue;
} catch (e) {
console.error(e);
}
if (Capacitor.getPlatform() === 'ios') {
return CookieManagerPlugin.setCookie({
url: window.location.host,
key,
value,
});
}
return Http.setCookie({
url: url(),
key,
value,
});
};
export const hasCookie = async (key) => {
const { Http } = Plugins;
const ret = await Http.getCookies({
url: url(),
key,
});
return ret?.value?.some((cookie) => key == cookie.key);
};
export const getCookie = async (key) => {
const { Http } = Plugins;
const ret = await Http.getCookies({
url: url(),
});
const value = ret?.value?.find((cookie) => key == cookie.key)?.value;
try {
const parsedValue = JSON.parse(value);
return parsedValue;
} catch (e) {
console.error(e);
return value;
}
};
export const deleteCookie = (key) => {
const { Http } = Plugins;
return Http.deleteCookie({
url: url(),
key,
});
}; As you can see, it mainly utilizes the https://github.com/capacitor-community/http plugin. However their Add import Capacitor
import Branch
@objc(CookieManagerPlugin)
public class CookieManagerPlugin: CAPPlugin {
@objc public func setCookie(_ call: CAPPluginCall) {
guard let key = call.getString("key") else {
return call.reject("Must provide key")
}
guard let value = call.getString("value") else {
return call.reject("Must provide value")
}
guard let urlString = call.getString("url") else {
return call.reject("Must provide URL")
}
guard let url = URL(string: urlString) else {
return call.reject("Invalid URL")
}
let jar = HTTPCookieStorage.shared
let cookieProperties: [HTTPCookiePropertyKey : Any] = [.name : key,
.value : value,
.domain : urlString,
.originURL : urlString,
.path : "/",
.version : "0",
.expires : Date().addingTimeInterval(2629743)
]
if let cookie = HTTPCookie(properties: cookieProperties) {
jar.setCookie(cookie)
}
call.resolve()
}
} together with #import <Capacitor/Capacitor.h>
CAP_PLUGIN(CookieManagerPlugin, "CookieManagerPlugin",
CAP_PLUGIN_METHOD(setCookie, CAPPluginReturnPromise);
) Both client and server side cookies will now fully work. BUT, you will have to get and set them through the just created import { setCookie, getCookie } from 'CookiesPlugin.js'
async () => {
await setCookie('foo', 'bar');
const result = await getCookie('foo');
console.log(result); // outputs 'bar'
} This means, that some third-party stuff will probably not work. See for example #1433 where Google Tag Manager (GTM) is not working. This is because GTM tries to set and get cookies by utilizing the So another problem we need to tackle. I came up with an idea. It is partly working already, but Capacitor its core needs to be changed for this as well. So what's the solution? Apparently it's possible to proxy the This makes it possible to do something like this: Object.defineProperty(document, 'cookie', {
set: function (val) {
const values = val.split(/=(.+)/);
if (values && values.length > 1) {
setCookie(values[0], values[1]);
}
},
}); What this does is the following: it replaces the browsers native setter for However, as I said before, there is still one catch: It would be possible to do something like this: Object.defineProperty(document, 'cookie', {
get: function (val) {
const cookies = await getCookies();
// parse the cookies, so they conform with the native document.cookie format
return cookies;
},
}); But that |
As it was said above, if you have issues that cookies are not attached to requests for ios because they are made to some other domain (not localhost), just use this advice: ionic-team/cordova-plugin-ionic-webview#22 (comment) But it works only for one domain, probably that one which you own, not third-party ones And you also can get errors with some service like Stripe which require https to be used |
Can you please take a look at this issue and update us about the priority and effort you guys are willing to put into this? |
Thanks @thomasvidas for the reply, makes sense. Just confirming I was not able to get Would be interested in hearing from anyone that has. |
This only pushes developers to use more complected methods of achieving the same result, and for Capacitor, something that should be working out of the box. In most cases developers only want to replicate "same site" cookie used in their web application, in their hybrid application.
Tokens still require a safe place to be stored, i.e. cookies. (UPDATE: Oh you meaning storing these first party cookies) |
Seconded |
Is anyone aware of an exemplary public repo where the client- and server-side setting of cookies works? @tafelnl you mentioned that with the workaround you described you're able to set cookies client- and server-side. Is that still the case? I'm not able to get it working. I've set the hostname in the capacitor config, am using the Http Plugin in combination with the native CookieManagerPlugin you described above and I set a respective AppBoundDomain, but it doesn't work for me. Do your workarounds still work for you? |
I fixed the issue by overwriting the cookie setter and getter functions and persisting the cookies by using the Capacitor Storage plugin. This will not solve 3rd party cookie issues, but it helped me to persist specific cookies I had to store in my application. import { Injectable } from '@angular/core';
import { Storage } from '@capacitor/storage';
import { BehaviorSubject } from 'rxjs';
const COOKIES_CACHE_KEY = 'COOKIES_CACHE';
@Injectable({
providedIn: 'root',
})
export class CookieStorageService {
private _cookies = new BehaviorSubject<string>('');
private _ready = new BehaviorSubject<boolean>(false);
public ready = this._ready.asObservable();
constructor() {
this.loadCookiesFromStorage();
this._cookies.subscribe((value) => this.persistCookies(value));
}
handleCookies() {
Object.defineProperty(document, 'cookie', {
set: (value) => this.saveCookie(value),
get: () => this.getCookies(),
});
}
private saveCookie(value: string) {
const values = value.split(/=(.+)/);
if (values && values.length > 1) {
let currentCookies = this._cookies.value;
currentCookies += value;
this._cookies.next(currentCookies);
}
}
private getCookies() {
return this._cookies.value;
}
private persistCookies(value: string) {
return Storage.set({ key: COOKIES_CACHE_KEY, value });
}
private async loadCookiesFromStorage() {
const { value } = await Storage.get({ key: COOKIES_CACHE_KEY });
this._cookies.next(value ?? '');
if (!this._ready.value) {
this._ready.next(true);
}
}
} export class AppComponent {
...
// calling our 3rd party service that requires local stored cookies
this.cookieStorageService.ready.pipe(filter((value) => value)).subscribe(() => this.sourcePointService.displayConsentMessage()); |
Thanks, a great workaround without waiting for |
@gerritvanaaken can you share if and how you made it work on the ios simulator? I don't have a hostname, just localhost and cannot make it work |
@avaleriani it's all about this file: More info on it here: https://capacitorjs.com/docs/config#schema You have to add this entry to the capacitor.config.json: "server": {
"hostname": "app-specific-subdomain.example.com"
}
Assuming your main backend is running on https://example.com, this way on iOS the schema based on capacitor:// will work and on android you avoid the problem that appears when using the same domain that is used for the hostname (that impacts on how android try to load the url, because it understand is a local one) Hope it helps. |
@qmarcos Thank you for your very clear explanation. Works well for production. For IOS simulator I ended up using |
For situations where you need cookie authentication in requests in your ionic app, the Community HTTP Plugin worked for me. I could not successfully set cookies on iOS even with that plugin, but it does allow you to manually set the cookie header, which no javascript api will. So you can do this: import { Http, HttpResponse } from '@capacitor-community/http';
...
somePostRequest(): Promise<HttpResponse> {
...
const options = {
url,
headers: { cookie: `myAuth=${token}` },
data: {},
};
return Http.post(options);
} |
Now that Capacitor v4 is released, has this been fixed? I cannot really find what's new in Cap v4 in comparison to Cap v3? |
Two things:
|
Congratulations on that 🥳 ! Thanks for taking the time to answer my questions despite that.
Makes sense, I'll be waiting patiently ;) |
I have a theoretical question. Many libraries or external services of course do not use some Http Plugin from Capacitor or the Cookies plugin to store cookies. Nevertheless, there are many of those that also drop necessary cookies. Now when 4.1 is released: Will this all work "out of the box" or only when I use the plugins provided by Capacitor, so implement stuff myself? Thank you! :) |
is there any updates on this as we're now at version 4.3.0 ? |
Even after using CapacitorCookie and CapacitorHttp plugins, I'm still having an issue. Anyone successfully got it working? |
@tsenguunchik Pointing to a server where the app is hosted is currently the easiest solution. Although not recommended, since it does not really fulfill the purpose of a "native" app. |
Cookies appear to work as expected for me in a new Capacitor app with CapacitorCookies enabled as of 4.6.x, with 4.6.0 being the first to correctly set and persist cookies on iOS/WKWebView thanks to this commit. Curious if other folks still see issues in apps created using 4.6.0 or later. |
EDIT/UPDATE: So I removed the WKAppBoundDomains from the info.plist that I had put there, and now both setting and getting cookies works. The cookies are readable when setting the cookie locally and also using the set-cookie header in a request. For context, I have set a hostname which is a subdomain of the cookie domain. No other config changes, no changes to the info.plist either. @jwisser-heap I'm still getting the same issues on 4.6.1. Latest versions of the http and cookies plugins too. IOS, cant read any cookies. set-cookie header works and I can see it in the cookies tab in safari inspector but it cant be read. |
@cmubo I am confused how you are able to read cookies set by the server. I wish I could but I don't understand how this will ever be possible with the From what I understand This is a problem for me because we have our mobile app making api requests to a Django backend at The session cookie is set by the server and is sent back to the server every time we make an API call. This works great. The session is unreadable by The CSRF cookie is set by the server. We set the DOMAIN attribute correctly to subdomains are considered the same domain. However do CSRF we need to read the cookie and send that value as a header with each post request. Unfortunately this only works on Android because on iOS the schema doesn't match and the cookie is considered a third party cookie and is unreadable. [UPDATE] However, for some reason the |
Is there any updates to this issue? I'm still not able to set cookies in iOS 16 on version 5.2.2. |
Reads like a show-stopper... any update? We are on 5.0.0 - still not working... |
In our case, when using Browser plugin, the main problem is that it uses SFSafari instead of ASWebAuthenticationSession to share cookies and so on. So we cannot do any SSO with this plugin to share server cookies. |
My app uses session/cookie based authentication I was having issues with cookies being persisted between app restarts on iOS. I solved this issue by simply importing the CapacitorCookies module in my root component (App.vue).
I think that by importing the package, some kind of initialization/restoring of cookies occurs. Or maybe it doesn't get bundled properly by vite if it's not defined as an import. You don't need to actually use and methods of the package, and it will show as unused in your IDE. I'm using Capacitor 5 (I'm using 5.4.2 to be specific) with Vue 3 and vite, hope this helps. The rest of my config is as follows:
|
Hello, is there any news on this issue? I have this problem with android, tested with multiple versions. The main issue seems to be solved in capacitor 6, at least for the newer android versions from API 33 upwards, the others versions still have the issue, which is not understandable. Does anyone has any workarounds for that? I always read about using http and cookies plugin, but the docs are not really intuitive. Can someone give some example on where exactly or how exactly to do the xhr request with the saved cookies? |
+1 I'm employing an HTTP-only cookie to securely access an API that demands authentication. According to the documentation, enabling the I'm unsure if I'm missing something... or if somebody could assist here |
Hi,
in Ios platform, Capacitor use the vkWebView, which has the well known problem of cookies shared between the domains, but no solution has been implemented.
In "cordova-plugin-wkwebview-engine", the ploblem is solved using "WKProcessPool":
"This plugin creates a shared WKProcessPool which ensures the cookie sharing happens correctly across WKWebView instances. CDVWKProcessPoolFactory class can be used to obtain the shared WKProcessPool instance if app creates WKWebView outside of this plugin."
Is possible add this on Capacitor? Or are there any workaround for do this manually?
Regards
The text was updated successfully, but these errors were encountered: