Reputation: 1957
I have an application that needs to check whether the client browser has third-party-cookies enabled. Does anyone know how to do this in JavaScript?
Upvotes: 77
Views: 85790
Reputation: 1
i think you can using this solution for detect whether 3rd party cookie.
If browser block 3rd cookie, you can't set cookie. so you can set a flag in cookie like is_block_3rd_cookie and get value of this key. If you can't get value -> it's mean browser block 3rd cookie, otherwise if you can get value, browser is support 3rd cookie
document.cookie = "is_block_3rd_cookie=true; SameSite=None; Secure"
if(document.cookie){
console.log('3rd cookie is enable')
} else {
console.log('3rd cookie is disabled')
}
Upvotes: 0
Reputation: 1646
This is something that I did to check if third-parties cookies have been blocked by the user.
I just try to access the browser's local storage. If the user has enabled third-party cookies then it should be available, else it would throw an error.
try {
if(window.localStorage) {
//cookies enabled
}
} catch (err) {
//cookies disabled
}
Upvotes: 0
Reputation: 6940
Alan H's solution is great, but you don't have to use PHP or any other server-side programming language.
At least if you use nginx. :)
This is a pure* nginx server-side configuration for Alan's solution:
server {
listen 80;
server_name third-party.example.com
# Don't allow user's browser to cache these replies
expires -1;
add_header Cache-Control "private";
etag off;
# The first third-party "JavaScript file" - served by nginx
location = /step1.js.php {
add_header Content-Type 'application/javascript; charset=UTF-8';
add_header Set-Cookie "third_party_c_t=hey there!;Max-Age=172800";
return 200 'window._3rd_party_test_step1_loaded();';
}
# The second third-party "JavaScript file" - served by nginx
location = /step2.js.php {
add_header Content-Type 'application/javascript; charset=UTF-8';
set $test 'false';
if ($cookie_third_party_c_t = 'hey there!') {
set $test 'true';
# clear the cookie
add_header Set-Cookie "third_party_c_t=;expires=Thu, 01 Jan 1970 00:00:00 GMT";
}
return 200 'window._3rd_party_test_step2_loaded($test);';
}
}
Side notes:
third-party-cookies.html
),server
section (scope) of the config - I kept it like this to make it more like Alan H's solution.Upvotes: 8
Reputation: 125
Steps to check if third party cookies are enabled with Greg and Alan's solutions:
I modify the files as my only need was to check if third party cookies are enabled or not, depends on that i would do something like route them to a page telling users to enable third party cookies.
(In debian 9 is in /etc/nginx/sites-enabled/default)
$ sudo nano /etc/nginx/sites-enabled/default
You'll need to have TLS/SSL on your domain or else you won't be able to set cookies from a third party domain and you'll get an error saying:
Because a cookie’s SameSite attribute was not set or is invalid, it defaults to SameSite=Lax, which prevents the cookie from being sent in a cross-site request. This behavior protects user data from accidentally leaking to third parties and cross-site request forgery. Resolve this issue by updating the attributes of the cookie: Specify SameSite=None and Secure if the cookie should be sent in cross-site requests. This enables third-party use. Specify SameSite=Strict or SameSite=Lax if the cookie should not be sent in cross-site requests.
Specify your allowed domains in "Access-Control-Allow-Origin", not recomended to leave it with "*" (public access).
You can specify 'Access-Control-Allow-Methods "GET";' as is the only method being used.
(I set those headers with "*" (public) just to make sure it works, after that, you can edit them.)
You can change the name of the endpoints (step1.js.php & step2.js.php) but you'll need to change it in you js script. (Requests are made to your-custom-domain.com/step1.js.php o your-custom-domain.com/step2.js.php unless you change it. The extension does not really matter, you can change it for "step1" and "step2" or whatever you like)
# Nginx config start
server {
server_name your-custom-domain.com;
# Check if third party cookies are allowed
# The first third-party "JavaScript file" - served by nginx
location = /step1.js.php {
expires -1;
add_header Cache-Control "private";
etag off;
add_header Access-Control-Allow-Origin "*";
add_header Access-Control-Allow-Methods "*";
add_header Content-Type 'application/javascript; charset=UTF-8';
add_header Set-Cookie "third_party_c_t=hey there!;Max-Age=172000; Secure; SameSite=none";
return 200 'window._3rd_party_test_step1_loaded();';
}
# The second third-party "JavaScript file" - served by nginx
location = /step2.js.php {
add_header Access-Control-Allow-Origin "*";
add_header Access-Control-Allow-Methods "*";
add_header Content-Type 'application/javascript; charset=UTF-8';
set $test 'false';
if ($cookie_third_party_c_t = 'hey there!') {
set $test 'true';
# clear the cookie
add_header Set-Cookie "third_party_c_t=;expires=Thu, 01 Jan 1970 00:00:00 GMT; Secure; SameSite=none";
}
return 200 'window._3rd_party_test_step2_loaded($test);';
}
# managed by Certbot, here is where your certificates goes.
listen [::]:443 ssl ipv6only=on;
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/www.couchdb.me/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/www.couchdb.me/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
Save it (ctrl+x - Press letter Y if you want to save it - Enter to confirm) and restart/reload your nginx:
$ sudo systemctl restart nginx.service
You can change the name of the methods (_3rd_party_test_step1_loaded and _3rd_party_test_step2_loaded) but you'll need to change it also in nginx's config. (Make sure names are unique)
2.1) Add this cript to the header of your html (this must be loaded first):
<script type="text/javascript">
window._3rd_party_test_step1_loaded = function () {
// At this point, a third-party domain has now attempted to set a cookie (if all went to plan!)
var step2El = document.createElement("script");
const url = your-custom-domain.com + "/step2.js.php";
step2El.setAttribute("src", url);
document.head.appendChild(step2El);
};
window._3rd_party_test_step2_loaded = function (cookieSuccess) {
// If true, the third-party domain cookies are enabled
// If false, the third-party domain cookies are disable
cookieSuccess ? callMethodIfTrue() : callMethodIfFalse();
};
</script>
2.2) Add the script at the end of your body html:
<script type="text/javascript" src="https://your-custom-domain/step1.js.php"></script>
or if your are working in a js file (remember that you need to have your file added on your html landing page eg:
<script type="text/javascript" src="path/to/your/js/file"></script>
JS file:
window._3rd_party_test_step1_loaded = function () {
// At this point, a third-party domain has now attempted to set a cookie (if all went to plan!)
var step2El = document.createElement("script");
const url = that.$url + "/step2.js.php";
step2El.setAttribute("src", url);
document.head.appendChild(step2El);
};
window._3rd_party_test_step2_loaded = function (cookieSuccess) {
// If true, the third-party domain cookies are enabled
// If false, the third-party domain cookies are disable
cookieSuccess ? callMethodIfTrue() : callMethodIfFalse();
};
window.onload = function () {
const url = "your-custom-domain.com" + "/step1.js.php";
var step1El = document.createElement("script");
step1El.setAttribute("src", url);
document.body.appendChild(step1El);
};
Upvotes: 0
Reputation: 1251
Here's a pure JS solution not requiring any server-side code, so it can work from a static CDN: https://github.com/mindmup/3rdpartycookiecheck - the first script sets the cookie in the code, then redirects to a second script that will post a message to the parent window.
You can try out a live version using https://jsfiddle.net/tugawg8y/.
Note, this demo does not seem to work any more. Maybe the window.postMessage
call is being blocked.
client-side HTML:
third party cookies are <span id="result"/>
<iframe src="https://mindmup.github.io/3rdpartycookiecheck/start.html"
style="display:none" />
client-side JS:
var receiveMessage = function (evt) {
if (evt.data === 'MM:3PCunsupported') {
document.getElementById('result').innerHTML = 'not supported';
} else if (evt.data === 'MM:3PCsupported') {
document.getElementById('result').innerHTML = 'supported';
}
};
window.addEventListener("message", receiveMessage, false);
Of course, this requires that the client runs JavaScript which is a downside compared to the server-based solutions; on the other hand, it's simpler, and you were asking about a JS solution.
Upvotes: 60
Reputation: 1537
My solution works by loading a <script> from an external domain that sets a cookie, checks if it was successful, and then passes the result (1 or 0) as an argument to a callback function.
The HTML:
<script>
function myCallback(is_enabled) {
if (is_enabled===1) {//third party cookies are enabled
}
}
</script>
<script src="https://third-party-domain/third-party-cookies.php?callback=myCallback"></script>
If you prefer to run it asynchronously, you may use the async and defer attributes.
This also works with jQuery:
<script>
$.ajax({
url: 'https://third-party-domain/third-party-cookies.php',
dataType: 'jsonp',
}).done(function(is_enabled) {
if (is_enabled===1) {//third party cookies are enabled
}
})
</script>
Here is the third-party-cookies.php code. This must be hosted on a different domain. The server must support PHP:
<?php
header('Cache-Control: no-store');
header('Content-Type: text/javascript');
if ($_GET['callback']=='') {
echo 'alert("Error: A callback function must be specified.")';
}
elseif (!isset($_GET['cookieName'])) {// Cookie not set yet
$cookieName = strtr((string)$_SERVER['UNIQUE_ID'], '@', '_');
while (isset($_COOKIE[$cookieName]) || $cookieName=='') {
$cookieName = dechex(mt_rand());// Get random cookie name
}
setcookie($cookieName, '3rd-party', 0, '/');
header('Location: '.$_SERVER['REQUEST_URI'].'&cookieName='.$cookieName);
}
elseif ($_COOKIE[$_GET['cookieName']]=='3rd-party') {// Third party cookies are enabled.
setcookie($_GET['cookieName'], '', -1, '/'); // delete cookie
echo $_GET['callback'].'(1)';
}
else {// Third party cookies are not enabled.
echo $_GET['callback'].'(0)';
}
Upvotes: 3
Reputation: 3086
Third Party Cookie detection with Whitelisting of URLs
Alan H & Gojko Adzic are good enough for majority of use cases, but these solutions won't work if you want your users to do whitelisting of third party cookies only to certain domains.
I'm presenting a slightly modified version of Gojko Adzic's answer
For this we need two domains :
tpc=pending
, then it redirects to Domain 2tpc=true
and redirects back to Domain 1tpc
and checks if its true
, if we get value as true, third party cookies are allowed if it is still in pending
third party cookies are blocked blocked.Now, you might ask your users to whitelist (allow third party cookies) ONLY for Domain 1
, also by this technique third party detection will be accurate
if the users had white listed your domain.
This is tested in Chrome 74, 75, 76 & Edge 78
Unfortunately, Mozilla doesn't provide whitelisting of urls like Chrome does and Safari has it own mechanisms of detecting third party cookies (ITP).
P.S. Will upload this demo in my github when I get time.
Upvotes: 1
Reputation: 16568
The third party sets & reads cookies over HTTP (not in JavaScript).
So we need two requests to an external domain to test if third-party cookies are enabled:
We cannot use XMLHTTPRequest (Ajax) because of the DOM security model.
Obviously you can't load both scripts in parallel, or the second request may be made before the first request’s response makes it back, and the test cookie(s) will not have been set.
Given:
The .html
file is on one domain, and
The .js.php
files are on a second domain, we have:
Saved as third-party-cookies.html
<!DOCTYPE html>
<html>
<head id="head">
<meta charset=utf-8 />
<title>Test if Third-Party Cookies are Enabled</title>
<style type="text/css">
body {
color: black;
background: white none;
}
.error {
color: #c00;
}
.loading {
color: #888;
}
.hidden {
display: none;
}
</style>
<script type="text/javascript">
window._3rd_party_test_step1_loaded = function(){
// At this point, a third-party domain has now attempted to set a cookie (if all went to plan!)
var step2Url = 'http://third-party.example.com/step2.js.php',
resultsEl = document.getElementById('3rd_party_cookie_test_results'),
step2El = document.createElement('script');
// Update loading / results message
resultsEl.innerHTML = 'Stage one complete, loading stage 2…';
// And load the second part of the test (reading the cookie)
step2El.setAttribute('src', step2Url);
resultsEl.appendChild(step2El);
}
window._3rd_party_test_step2_loaded = function(cookieSuccess){
var resultsEl = document.getElementById('3rd_party_cookie_test_results'),
errorEl = document.getElementById('3rd_party_cookie_test_error');
// Show message
resultsEl.innerHTML = (cookieSuccess ? 'Third party cookies are <b>functioning</b> in your browser.' : 'Third party cookies appear to be <b>disabled</b>.');
// Done, so remove loading class
resultsEl.className = resultsEl.className.replace(/\bloading\b/,' ');
// And remove error message
errorEl.className = 'hidden';
}
</script>
</head>
<body id="thebody">
<h1>Test if Third-Party Cookies are Enabled</h1>
<p id="3rd_party_cookie_test_results" class='loading'>Testing…</p>
<p id="3rd_party_cookie_test_error" class="error hidden">(If this message persists, the test could not be completed; we could not reach the third-party to test, or another error occurred.)</p>
<script type="text/javascript">
window.setTimeout(function(){
var errorEl = document.getElementById('3rd_party_cookie_test_error');
if(errorEl.className.match(/\berror\b/)) {
// Show error message
errorEl.className = errorEl.className.replace(/\bhidden\b/,' ');
} else {
}
}, 7*1000); // 7 sec timeout
</script>
<script type="text/javascript" src="http://third-party.example.com/step1.js.php"></script>
</body>
</html>
Saved as step1.js.php
This is written in PHP so we can set cookies as the file loads. (It could, of course, be written in any language, or even done in server config files.)
<?php
header('Content-Type: application/javascript; charset=UTF-8');
// Set test cookie
setcookie('third_party_c_t', 'hey there!', time() + 3600*24*2);
?>
window._3rd_party_test_step1_loaded();
Saved as step2.js.php
This is written in PHP so we can read cookies, server-side, before we respond. We also clear the cookie so the test can be repeated (if you want to mess around with browser settings and re-try).
<?php
header('Content-Type: application/javascript; charset=UTF-8');
// Read test cookie, if there
$cookie_received = (isset($_COOKIE['third_party_c_t']) && $_COOKIE['third_party_c_t'] == 'hey there!');
// And clear it so the user can test it again
setcookie('third_party_c_t', '', time() - 3600*24);
?>
window._3rd_party_test_step2_loaded(<?php echo ($cookie_received ? 'true' : 'false'); ?>);
The last line uses the ternary operator to output a literal Javascript true
or false
depending on whether the test cookie was present.
Available for your testing pleasure at https://alanhogan.github.io/web-experiments/3rd/third-party-cookies.html.
(As a final note — don’t use someone else’s server to test third-party cookies without their permission. It could break spontaneously or inject malware. And it’s rude.)
Upvotes: 78
Reputation: 360592
In theory you'd just have a page call out somewhere that would set a thirdparty cookie and then check for that cookie's existence. However, standard browser security does not allow scripts from domain A to do anything with cookies set on domains B,C,etc... e.g. you can't access "foreign" cookies.
If you have some specific usage in mind, such as checking if ads are blocked (which would also block the 3rd party tracking cookie), you could check if the ad server's content is within the page's DOM, but you couldn't see if the cookie's there.
Upvotes: 1