Ask the user for cookies access when running in iframe in Safari (#1862)

This commit is contained in:
Jonatan Kłosko 2023-04-13 20:39:25 +02:00 committed by GitHub
parent 6ebcdf20b4
commit 8e02e0154d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -18,43 +18,97 @@ import { loadAppAuthToken } from "./lib/app";
import { settingsStore } from "./lib/settings";
import { registerTopbar, registerGlobalEventHandlers } from "./events";
const csrfToken = document
.querySelector("meta[name='csrf-token']")
.getAttribute("content");
function connect() {
const csrfToken = document
.querySelector("meta[name='csrf-token']")
.getAttribute("content");
const liveSocket = new LiveSocket(
window.LIVEBOOK_BASE_URL_PATH + "/live",
Socket,
{
params: (liveViewName) => {
return {
_csrf_token: csrfToken,
// Pass the most recent user data to the LiveView in `connect_params`
user_data: loadUserData(),
app_auth_token: loadAppAuthToken(),
};
},
hooks: hooks,
dom: morphdomOptions,
}
);
const liveSocket = new LiveSocket(
window.LIVEBOOK_BASE_URL_PATH + "/live",
Socket,
{
params: (liveViewName) => {
return {
_csrf_token: csrfToken,
// Pass the most recent user data to the LiveView in `connect_params`
user_data: loadUserData(),
app_auth_token: loadAppAuthToken(),
};
},
hooks: hooks,
dom: morphdomOptions,
}
);
// Show progress bar on live navigation and form submits
registerTopbar();
// Show progress bar on live navigation and form submits
registerTopbar();
// Handle custom events dispatched with JS.dispatch/3
registerGlobalEventHandlers();
// Handle custom events dispatched with JS.dispatch/3
registerGlobalEventHandlers();
// Reflect global configuration in attributes to enable CSS rules
settingsStore.getAndSubscribe((settings) => {
document.body.setAttribute("data-editor-theme", settings.editor_theme);
});
// Reflect global configuration in attributes to enable CSS rules
settingsStore.getAndSubscribe((settings) => {
document.body.setAttribute("data-editor-theme", settings.editor_theme);
});
// Connect if there are any LiveViews on the page
liveSocket.connect();
// Connect if there are any LiveViews on the page
liveSocket.connect();
// Expose liveSocket on window for web console debug logs and latency simulation:
// >> liveSocket.enableDebug()
// >> liveSocket.enableLatencySim(1000) // enabled for duration of browser session
// >> liveSocket.disableLatencySim()
window.liveSocket = liveSocket;
// Expose liveSocket on window for web console debug logs and latency simulation:
// >> liveSocket.enableDebug()
// >> liveSocket.enableLatencySim(1000) // enabled for duration of browser session
// >> liveSocket.disableLatencySim()
window.liveSocket = liveSocket;
}
// When Livebook runs in a cross-origin iframe the browser may restrict access
// to cookies. This is the case in Safari with the "Prevent cross-site tracking"
// option enabled, which is the default. Without cookies access, the session
// is not stored, so CSRF tokens are invalid. Consequently, LV keeps reloading
// the page, as we try to connect the socket with invalid token. To work around
// this we need to ask to explicitly grant access to cookies, as outlined in (1).
//
// (1): https://developer.mozilla.org/en-US/docs/Web/API/Storage_Access_API
if (document.hasStorageAccess) {
document.hasStorageAccess().then((hasStorageAccess) => {
if (hasStorageAccess) {
connect();
} else {
const overlayEl = document.createElement("div");
overlayEl.innerHTML = `
<div class="fixed top-0 bottom-0 left-0 right-0 z-[1000] px-4 py-8 bg-gray-900/95 flex justify-center items-center">
<div class="max-w-[600px] w-full flex flex-col">
<div class="text-xl text-gray-100 font-medium">
Action required
</div>
<div class="mt-3 text-sm text-gray-300">
It looks like Livebook does not have access to cookies. This usually happens when
it runs in an iframe. To make the app functional you need to grant Livebook access
to its cookies explicitly.
</div>
<div>
<button id="grant-access" class="mt-6 button-base button-blue">
Grant access
</button>
</div>
</div>
</div>
`;
document.body.appendChild(overlayEl);
const grantAccessButtonEl = overlayEl.querySelector("#grant-access");
grantAccessButtonEl.addEventListener("click", (event) => {
document
.requestStorageAccess()
.then(() => window.location.reload())
.catch(() => console.log("Access to storage denied"));
});
}
});
} else {
connect();
}