This is an interesting question. There should be many ways of doing it, the first thing that comes to my mind is to use an iframe
. The example below is tested with Django 2.2
.
In your settings.py
, expose your sessionid
to javascript.
SESSION_COOKIE_HTTPONLY = False
In your view, be sure to put xframe_options_exempt
on, or django will not allow it to be "iframed" from another domain, here I use a template view, so I put the decorator in urls.py
instead.
from django.views.decorators.clickjacking import xframe_options_exempt
urlpatterns = [
path(
'other_domain/',
xframe_options_exempt(TemplateView.as_view(template_name='examplesite/otherdomain.html')),
name='other_domain',
)
# ...
]
domains
is a list of all of the other domains (not including the one your user is on right now), in your template, expose them in the <head>
tag.
<head>
{{ domains|json_script:"domains" }}
{{ other_domain_path|json_script:"other-domain-path"}}
</head>
this will become something like this:
<script id="domains" type="application/json">["c222dbef.ngrok.io"] </script>
<script id="other-domain-path" type="application/json">"/other_domain/"</script>
Then in your javascript:
(function() {
function getCookie(cname) { //copied from w3schools
var name = cname + "=";
var decodedCookie = decodeURIComponent(document.cookie);
var ca = decodedCookie.split(";");
for (var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == " ") {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return "";
}
function postSessionID(id) {
var domains = JSON.parse(document.getElementById("domains").textContent);
var path = JSON.parse(document.getElementById("other-domain-path").textContent);
domains.forEach(function(domain) {
var src = "https://" + domain + path;
var iframeEl = document.createElement("iframe");
iframeEl.setAttribute("class", "invisible");
iframeEl.setAttribute("src", src);
(function(id) { // this is an async call in a loop, create a closure here to protect "id"
iframeEl.addEventListener("load", function() {
this.contentWindow.postMessage(id, this.getAttribute("src"));
});
})(id);
document.body.appendChild(iframeEl);
});
}
function main() {
var sessionID = getCookie("sessionid");
if (!sessionID) {
return;
}
postSessionID(sessionID);
}
main();
})();
The idea of the above code is to create iframes for each other domains, the src of the iframes are pointing to our view
named "other_domain". Once the iframes are loaded, we use postMessage
to send session id to them.
In examplesite/otherdomain.html
:
<head>
{{ domains|json_script:"domains" }}
{# we also need to expose all other domains #}
</head>
in your script:
(function() {
function setCookie(cname, cvalue, exdays) {
var d = new Date();
d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toUTCString();
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
}
var domains = JSON.parse(document.getElementById("domains").textContent);
var trustedSources = domains.map(function(domain) {
return "https://" + domain;
});
window.addEventListener("message", function(e) {
if (!e.origin in trustedSources) {
return; // this prevents setting session id from other source
}
var sessionID = e.data;
// you can probably get your cookie expiry from your django view, so all of your cookie expires at the same time
setCookie("sessionid", sessionID, 365);
}, false);
})();
Now your users can log in and log out from any of your domains, and they'll have the same session across all of your domains.
I'm posting the full example in my github: https://github.com/rabbit-aaron/django-multisite-sign-in
Follow readme.md
to set it up.