Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Safari iframe cookie workaround
<?php
header("Location: " . $_GET['redirect']);
setcookie("__trust");
<?php
if (!isset($_COOKIE['testcookie']))
echo "Cookie not set!";
else if ($_COOKIE['testcookie'] != $_GET['cookie'])
echo "Cookies don't match: " . $_COOKIE['testcookie'] . ' != ' . $_GET['cookie'];
else
echo "Cookies match!";
<?php
$value = uniqid();
setcookie("testcookie", $value);
echo "<p>Now go <a href='dest_get.php?cookie=" . $value . "'>here</a></p>";
<?php
$internalPath = "http://local.test/path";
$externalPath = "http://remote.test/path";
?>
<html><head><title>Redirect Cookie Test</title></head>
<body>
<?php if ($_GET['redirected']): ?>
<p>iframe starts below...</p>
<iframe src="<?= $externalPath ?>/dest_set.php"?>
<?php else: ?>
<a href="<?= $externalPath ?>/dest_bounce.php?redirect=<?= $internalPath ?>/src.php?redirected=true">Bounce here...</a>
<?php endif; ?>
</body>
@kevinvos

This comment has been minimized.

Copy link

@kevinvos kevinvos commented Oct 27, 2017

Nice catch, thanks for the explanation! I've discovered the same problem which magically solved itself, now I know why and how :).

@RaiserWeb

This comment has been minimized.

Copy link

@RaiserWeb RaiserWeb commented Nov 20, 2017

This indeed works. One issue it presents is that Google Analytics tracking is lost, and counts the visit as being referred from the remote.test domain. I'm trying to work out how to get GA to ignore this redirection but no luck thus far.

@mattharley

This comment has been minimized.

Copy link

@mattharley mattharley commented Nov 29, 2017

Unsure if this still works but there is a Javasctipt-only solution https://github.com/vitr/safari-cookie-in-iframe

Works on the same principal but does the redirect on the page underneath the iFrame without needing to hit the server.

@Enalmada

This comment has been minimized.

Copy link

@Enalmada Enalmada commented Nov 30, 2017

I just put something in an iframe and was pulling my hair out that some users were reporting issues...turned out to be this. Thank you very much for taking the time to do this write up. Also very thankful for the "safari-cookie-in-iframe" reference....here is what I went with based on that:

PARENT

var is_safari = navigator.userAgent.indexOf("Safari") > -1;
// Chrome has Safari in the user agent so we need to filter (https://stackoverflow.com/a/7768006/1502448)
var is_chrome = navigator.userAgent.indexOf('Chrome') > -1;
if ((is_chrome) && (is_safari)) {is_safari = false;}  
if (is_safari) {
    // See if cookie exists (https://stackoverflow.com/a/25617724/1502448)
    if (!document.cookie.match(/^(.*;)?\s*fixed\s*=\s*[^;]+(.*)?$/)) {
        // Set cookie to maximum (https://stackoverflow.com/a/33106316/1502448)
        document.cookie = 'fixed=fixed; expires=Tue, 19 Jan 2038 03:14:07 UTC; path=/';
        window.location.replace("http://vit-demos.appspot.com/_safari_fix.html");
    }
}

CHILD

document.cookie = 'safari_cookie_fix=true; path=/';
window.location.replace(document.referrer);
@jloguercio

This comment has been minimized.

Copy link

@jloguercio jloguercio commented Jun 3, 2018

I can't make this work with the solution : https://github.com/vitr/safari-cookie-in-iframe , i create have an site B in a Iframe in A, so in A i write this code :

var is_safari = navigator.userAgent.indexOf("Safari") > -1;
// Chrome has Safari in the user agent so we need to filter (https://stackoverflow.com/a/7768006/1502448)
var is_chrome = navigator.userAgent.indexOf('Chrome') > -1;
if ((is_chrome) && (is_safari)) {is_safari = false;}  
if (is_safari) {
    // See if cookie exists (https://stackoverflow.com/a/25617724/1502448)
    if (!document.cookie.match(/^(.*;)?\s*fixed\s*=\s*[^;]+(.*)?$/)) {
        // Set cookie to maximum (https://stackoverflow.com/a/33106316/1502448)
        document.cookie = 'fixed=fixed; expires=Tue, 19 Jan 2038 03:14:07 UTC; path=/';
        window.location.replace("http://vit-demos.appspot.com/_safari_fix.html");
    }
}

and create a file called _safari_fix.html with this code :

document.cookie = 'safari_cookie_fix=true; path=/';
window.location.replace(document.referrer);

but the workaround doesn't work

@datthepirate

This comment has been minimized.

Copy link

@datthepirate datthepirate commented Jul 20, 2018

Does this still work in 2018?

@bktan81

This comment has been minimized.

Copy link

@bktan81 bktan81 commented Aug 1, 2018

tried and it works

@dawolf

This comment has been minimized.

Copy link

@dawolf dawolf commented Aug 6, 2018

This stopped working for me with the latest Safari on macOS Version 11.1.2 (13605.3.8)

Session cookie in a iframe will be dropped if the domain in the iframe has not been visited before.

@fbricenho

This comment has been minimized.

Copy link

@fbricenho fbricenho commented Sep 20, 2018

I just put something in an iframe and was pulling my hair out that some users were reporting issues...turned out to be this. Thank you very much for taking the time to do this write up. Also very thankful for the "safari-cookie-in-iframe" reference....here is what I went with based on that:

PARENT

var is_safari = navigator.userAgent.indexOf("Safari") > -1;
// Chrome has Safari in the user agent so we need to filter (https://stackoverflow.com/a/7768006/1502448)
var is_chrome = navigator.userAgent.indexOf('Chrome') > -1;
if ((is_chrome) && (is_safari)) {is_safari = false;}  
if (is_safari) {
    // See if cookie exists (https://stackoverflow.com/a/25617724/1502448)
    if (!document.cookie.match(/^(.*;)?\s*fixed\s*=\s*[^;]+(.*)?$/)) {
        // Set cookie to maximum (https://stackoverflow.com/a/33106316/1502448)
        document.cookie = 'fixed=fixed; expires=Tue, 19 Jan 2038 03:14:07 UTC; path=/';
        window.location.replace("http://vit-demos.appspot.com/_safari_fix.html");
    }
}

CHILD

document.cookie = 'safari_cookie_fix=true; path=/';
window.location.replace(document.referrer);

Does this still working?

@ricklambrechts

This comment has been minimized.

Copy link

@ricklambrechts ricklambrechts commented Nov 7, 2018

The javascript workaround still works!

@shazadmaved

This comment has been minimized.

Copy link

@shazadmaved shazadmaved commented Feb 23, 2019

With the javascript workaround, The first access gets into a redirect loop in safari :| , could anyone suggest something that must be wrong. The second access onwards it works fine. For some reason during the redirect the "fixed" cookie that is set is not being fetched.

@thomasdarde

This comment has been minimized.

Copy link

@thomasdarde thomasdarde commented Apr 3, 2019

this solution is nice and works , but an important caveat is found if the page is called from a "post" url. The return won't work.
window.postmessage is a good solution to share data between iframe and parent.

@allenlinli

This comment has been minimized.

Copy link

@allenlinli allenlinli commented Apr 17, 2019

Should we fire a radar to Apple for this Safari feature(bug)?

@allenlinli

This comment has been minimized.

Copy link

@allenlinli allenlinli commented Apr 17, 2019

I think maybe we can't... https://discussions.apple.com/thread/4156939
I just can't figure what could iframe cookie threat users using Safari.

@SebT

This comment has been minimized.

Copy link

@SebT SebT commented Aug 1, 2019

The JS workaround works on MacOS but not on iOS (iPad). Does anyone have a similar problem ?

@raj-uimatic

This comment has been minimized.

Copy link

@raj-uimatic raj-uimatic commented Jan 3, 2020

My cookie does not carry forward data from one iframe to another in safari, I have a scenario like this:
I have parent domain ex: website.com , Inside that I have a iframe with this url ex. parent.com where I am setting cookie for parent while submitting form so I am setting cookie like this domain=".parent.com" (so it will set cookie for subdomain automatically)

When form is submitted I move to another page like website2.com and inside that page I have a iframe with subdomain like parent.child.com Now issue is I am not able to get cookie which were set by parent.com in safari

Please help! other browser works great

Thanks

@tgiordmaina

This comment has been minimized.

Copy link

@tgiordmaina tgiordmaina commented Mar 2, 2020

It seems safari is the new IE.. what a weird behavior, I'm tired, 3 days I tried to solve that, I need to rebuild all my authentication system because of that. TY apple, if anyone as a solution not a workaround it will be great. Thanks

@WebSpectrum

This comment has been minimized.

Copy link

@WebSpectrum WebSpectrum commented Mar 16, 2020

Hi All - what is the best solution to date if you don't have control or ability to deploy code on site B ? ....

@tgiordmaina

This comment has been minimized.

Copy link

@tgiordmaina tgiordmaina commented Mar 16, 2020

I used a popup to do the trick (by temporary passing the cookie as parameter to a third party API).
If you have full control on site B (code + DNS) I advice you to declare a subdomain in you site B which forward silently to your wished website/API (add a new AAAA entry with your service IP) :

       +------------------+                          +-----------------------+
       |                  |   XHR with credentials   |                       |
       |  www.siteB.com   --------------------------->  api-siteA.siteB.com  |
       |                  |                          |                       |
       +------------------+                          +-----------------------+
@russau

This comment has been minimized.

@mwleinad

This comment has been minimized.

Copy link

@mwleinad mwleinad commented Apr 7, 2020

Any idea if there's a workaround with safari 13.1?

@antstorm

This comment has been minimized.

Copy link

@antstorm antstorm commented Apr 12, 2020

Looks like requesting storage access via their APIs is the way to do it — https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/

@Arcolye

This comment has been minimized.

Copy link

@Arcolye Arcolye commented Apr 29, 2020

What a fantastic time to put this extra hurdle on struggling and furloughed development teams. Thanks Apple, Safari, and specifically John Wilander!

With brick-and-mortar businesses struggling and software companies doing what they can to help out, NOW was the time to release this huge software-breaking change? Not 2022 like Chrome has roadmapped? Now? During the Covid recession? And with "Storage Access API" still in a poorly-documented and experimental phase? Now?

Badly done, Safari. Badly done indeed. Shame on you, John Wilander.

@asecondwill

This comment has been minimized.

Copy link

@asecondwill asecondwill commented May 1, 2020

The workarounds seem to work, but not in webkit view in eg facebook/twitter/instagram webviewer. And clues on what to do would be ace.

@stevenlawrance

This comment has been minimized.

Copy link

@stevenlawrance stevenlawrance commented May 5, 2020

From https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/, it appears that a workaround that uses OAuth2 may exist, though it's not exactly clear how to invoke this (and using Secure+HttpOnly cookies isn't sufficient to make it work). I'll have to poke around the WebKit source code..

Option 1: OAuth 2.0 Authorization with which the authenticating domain (in your case, the third-party that expects cookies) forwards an authorization token to your website which you consume and use to establish a first-party login session with a server-set Secure and HttpOnly cookie.

@github-xuser

This comment has been minimized.

Copy link

@github-xuser github-xuser commented May 19, 2020

I have a workaround based on an interface to sagepay.
We tell sagepay where to return to after CC entry in an iframe, Once the user submits the CC form to sagepay, they redirect the iframe to us and we take over control of the iframe and close it.
Unless its the latest safari. Then the iframe redirect happens, but with no session cookie so our software has no continuity and terminates because it thinks the session has timedout. Not very good when someone is trying to buy something in your shop.
I already implemented a change so that the iframe was created with a page from our domain which then redirects to sagepay, so i know that safari has the correct session cookie in the iframe. This did actually fix quite a lot of the problems but not all.
My thought for fixing them all was that safari doesnt like sending the correct session cookie when sagepay redirects back to our domain, but i wonder what will happen if i make the redirect from sagepay simply do another redirect from us, to us.
It worked, on the second redirect from our domain to our domain the session cookie is correct.
That has to be a bug in safari.
So if i am dom1.com and sagepay is dom2.com the fix is:
Create an iframe with src = dom1.com/redirect1 this simply contains html or javascript to do the correct redirect to your dom2.com page.
Tell dom2.com to return to dom1.com/dummyredirect. dummyredirect copies all the querystring or post data and returns a page that redirects back to dom1.com/theproperreturnaddress.
Its passed all our tests so far.
I hope it helps someone else, I can't see safari getting fixed or even acknowledging the bug anytime soon.

@xabier98

This comment has been minimized.

Copy link

@xabier98 xabier98 commented May 20, 2020

Hi

Please ¿can you explain or write an small example with your solution?

Thank you

Best Regards

@engelmav

This comment has been minimized.

Copy link

@engelmav engelmav commented May 26, 2020

What a fantastic time to put this extra hurdle on struggling and furloughed development teams. Thanks Apple, Safari, and specifically John Wilander!

With brick-and-mortar businesses struggling and software companies doing what they can to help out, NOW was the time to release this huge software-breaking change? Not 2022 like Chrome has roadmapped? Now? During the Covid recession? And with "Storage Access API" still in a poorly-documented and experimental phase? Now?

Badly done, Safari. Badly done indeed. Shame on you, John Wilander.

Fascinating. The economic impact of code changes... should be factored in with behemoth applications and firms like Safari/Apple.

@jhud

This comment has been minimized.

Copy link

@jhud jhud commented May 30, 2020

Hi everyone, commiserations to those of you who also got blindsided by this during all the other Covid-19 IT dramas. Anyway, here is my quick fix for this.

Note that you cannot nest the document.hasStorageAccess, otherwise it misses the user interaction, so I don't bother checking - I run the code every time. I dropped this code inside my iframe. It needs to be triggered from an onClick, ie:

 <div onclick="safariFix();">If you have problems logging in on Safari, please click here.</div>

But instead I call this directly from my login form:

  <input type="submit" onclick="safariFix();" id="login_submit_button" value="{% trans 'Log in' %}" />

The first login fails because Safari pops up a non-blocking "do you want to allow..." popup, but it'll suffice until there's a fix that won't pollute the UI with some "click this if you are on Safari" button.

<script>
  // Safari now suddenly blocks iframe cookie access, so we need to call this during some user interaction
  function safariFix() {
    if (navigator.userAgent.search("Safari") >= 0 && navigator.userAgent.search("Chrome") < 0) {
      document.requestStorageAccess().then(() => {
          // Now we have first-party storage access!
          console.log("Got cookie access for Safari workaround");
          // Let's access some items from the first-party cookie jar
          document.cookie = "foo=bar";              // drop a test cookie
        },  () => { console.log('access denied') }).catch((error) => {
          // error obtaining storage access.
          console.log("Could not get access for Safari workaround: " + error);
        });
      }
  }
</script>

Apologies for the messiness - it's Saturday night and I just need to rush this out now so my customers can start making money again.

@openfermentation

This comment has been minimized.

Copy link

@openfermentation openfermentation commented Jun 5, 2020

The economic impact of code changes... should be factored in with behemoth applications and firms like Safari/Apple.

Not just economic, but very real health impact. My company delivers healthcare related information to patients, including COVID-19 content. We act as a third party to health plans and it's an all too common practice for this industry to integrate within iframes.

As it stands, patients using Safari can't access our content due to this security change. Content like ours has been shown to improve health outcomes. If you use Safari you might suffer a worse outcome in the event of a health crisis.

looks like requesting storage access via their APIs is the way to do it

The storage access API in Safari will flat out reject requestStorageAccess() if the client hasn't visited the third-party in first-party context AND has interacted with the site — https://stackoverflow.com/questions/52173595/how-to-debug-safari-itp-2-0-requeststorageaccess-failure

Interestingly, Firefox's implementation doesn't appear to enforce this same requirement, only adding to the confusion.

The majority of our users don't know who we are and have never visited our domain directly. The Storage Access API is clearly designed to accommodate social media network workflows. The rest of us are getting steamrolled.

@lenusch

This comment has been minimized.

Copy link

@lenusch lenusch commented Jul 5, 2020

Does it work now? Is there a functional workarround? I didn't get this to work until now :-(

@sarayaz

This comment has been minimized.

Copy link

@sarayaz sarayaz commented Jul 8, 2020

Does anybody have solution for this in safari 13+ ?

@joostfaassen

This comment has been minimized.

Copy link

@joostfaassen joostfaassen commented Aug 18, 2020

None of these solutions worked for us. And the same issue started showing up in Google Chrome and other browsers (especially in incognito / private modes).

What we did is add an upstream location on the vhost of the site that embeds the other site. I.e. this adds https://mainsite/my-embedded-site/* that forwards requests to https://my-embedded-site/

This way both sites get served to the end user from the main domain (mainsite) making all cookies "first party" cookies.

This solves the problem for us on all combinations of platforms, browsers and incognito vs normal modes.

Additionally, make sure your "secure" and "samesite" cookie options are set correctly, and make sure the cookie names on the main + embedded sites differ (otherwise they keep thrashing eachother's sessions 😄 )

It does require that you control the embedding app's server, so this won't solve the situation for everybody.. but I hope it'll help some people in this thread!

@jhud

This comment has been minimized.

Copy link

@jhud jhud commented Aug 18, 2020

Note that Apple seems to keep closing off iFrame cookies with every Safari update and breaking my workarounds. I gave up and am moving to a JS library. It might be possible to pass cookies with postMessage, but in the end it'll be less screwing around to just do an API integration with the host sites.

@luxio

This comment has been minimized.

Copy link

@luxio luxio commented Aug 20, 2020

@joostfaassen Thank you for sharing your solution.

What we did is add an upstream location on the vhost of the site that embeds the other site. I.e. this adds https://mainsite/my-embedded-site/* that forwards requests to https://my-embedded-site/

Could you share your server configuration to create the upstream location?

@JamesMcMurrin

This comment has been minimized.

Copy link

@JamesMcMurrin JamesMcMurrin commented Aug 20, 2020

I tried a similar script to this, and IE 11 died just from having the document.requestStorageAccess in the script. Never mind that it'll never reach it (or that I put it in a try catch), it throws a syntax error just seeing it.

The first login fails because Safari pops up a non-blocking "do you want to allow..." popup, but it'll suffice until there's a fix that won't pollute the UI with some "click this if you are on Safari" button.

<script>
  // Safari now suddenly blocks iframe cookie access, so we need to call this during some user interaction
  function safariFix() {
    if (navigator.userAgent.search("Safari") >= 0 && navigator.userAgent.search("Chrome") < 0) {
      document.requestStorageAccess().then(() => {
          // Now we have first-party storage access!
          console.log("Got cookie access for Safari workaround");
          // Let's access some items from the first-party cookie jar
          document.cookie = "foo=bar";              // drop a test cookie
        },  () => { console.log('access denied') }).catch((error) => {
          // error obtaining storage access.
          console.log("Could not get access for Safari workaround: " + error);
        });
      }
  }
</script>
@benross

This comment has been minimized.

Copy link

@benross benross commented Sep 9, 2020

Thanks for the many ideas in this thread!

Wanted to share we've been using a service to get around this issue and so far it has been working well for us: cloudcookie.io. We host content in 3rd party iframes and often don't have access to the parent (host) page. It's a commercial solution so might not be appropriate for all but pretty inexpensive (and has a free tier).

@lenusch

This comment has been minimized.

Copy link

@lenusch lenusch commented Sep 10, 2020

Thanks for the many ideas in this thread!

Wanted to share we've been using a service to get around this issue and so far it has been working well for us: cloudcookie.io. We host content in 3rd party iframes and often don't have access to the parent (host) page. It's a commercial solution so might not be appropriate for all but pretty inexpensive (and has a free tier).

Hi, i double checked everything but we have a PHP App and need a Session Cookie and this Javascript "CloudCookie" will not be able to pass Session Cookie to PHP, or am i mistaken? My Mate told me this would be not fit my needs. :-S

@jhud

This comment has been minimized.

Copy link

@jhud jhud commented Sep 10, 2020

I looked at some existing solutions, and big companies which rely on iframes appear to be passing the session tokens through PostMessage between the host page and the iframe.

But I am sick of doing these increasingly ugly hacks. IMO, iframes and 3rd party cookies are dead - Apple has just killed them a year or so earlier.

So I have converted my old iframe integrations to use my existing app REST API + JavaScript + local storage. My customers love this JS client-side integration compared to iframes, and it gives me a unified interface for my apps and web. It's a much more solid solution which will last for the ages.

I know you don't want to hear this if you are looking for a quick fix, but I suggest that you already start planning to ditch iframes + 3rd party cookies in the medium to long term.

@code2infiniteE

This comment has been minimized.

Copy link

@code2infiniteE code2infiniteE commented Sep 12, 2020

Thanks for the many ideas in this thread!
Wanted to share we've been using a service to get around this issue and so far it has been working well for us: cloudcookie.io. We host content in 3rd party iframes and often don't have access to the parent (host) page. It's a commercial solution so might not be appropriate for all but pretty inexpensive (and has a free tier).

Hi, i double checked everything but we have a PHP App and need a Session Cookie and this Javascript "CloudCookie" will not be able to pass Session Cookie to PHP, or am i mistaken? My Mate told me this would be not fit my needs. :-S

I also tried cloudcookie.io and have it working on a project. It's a front-end (javascript) cookie framework, so if you need the cookies on the server-side (eg PHP), you just need to add an ajax call or page redirect once you get the cookies from the front-end. (@lenusch)

@Benamin

This comment has been minimized.

Copy link

@Benamin Benamin commented Sep 17, 2020

@sparkdoo

This comment has been minimized.

Copy link

@sparkdoo sparkdoo commented Oct 19, 2020

So I have converted my old iframe integrations to use my existing app REST API + JavaScript + local storage. My customers love this JS client-side integration compared to iframes, and it gives me a unified interface for my apps and web. It's a much more solid solution which will last for the ages.

@jhud how are you dealing with the security implications of providing a client side javascript approach vs iframe? We considered both options but found the risk of running our javascript next to potentially malicious javascript too much of a concern to proceed, but of course as you say the new concern is that our existing solution will be completely blocked in the not so distant future

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.