mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-10-09 21:16:26 +08:00
Improve cookie access when running in iframe (#1888)
This commit is contained in:
parent
afe71a9bac
commit
8eb93f3e24
4 changed files with 81 additions and 37 deletions
|
@ -62,17 +62,39 @@ function connect() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// When Livebook runs in a cross-origin iframe the browser may restrict access
|
// 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"
|
// to cookies. Without cookies access, the session is not stored, so CSRF tokens
|
||||||
// option enabled, which is the default. Without cookies access, the session
|
// are invalid. Consequently, LV keeps reloading the page, as we try to connect
|
||||||
// is not stored, so CSRF tokens are invalid. Consequently, LV keeps reloading
|
// the socket with invalid token. To work around this we tell the user to open
|
||||||
// the page, as we try to connect the socket with invalid token. To work around
|
// Livebook outside the iframe.
|
||||||
// this we tell the user to open Livebook outside the iframe.
|
//
|
||||||
|
// The behaviour varies across browsers and browsing modes (regular and private).
|
||||||
|
// A few examples (at the time of writing):
|
||||||
|
//
|
||||||
|
// * Safari by default blocks all cross-origin cookies. This is controlled by
|
||||||
|
// the "Prevent cross-site tracking" option
|
||||||
|
//
|
||||||
|
// * Chrome in incognito mode blocks all cross-origin cookies, can be relaxed
|
||||||
|
// on per-site basis
|
||||||
|
//
|
||||||
|
// * Firefox implements state partitioning (1) and it is enabled for storage
|
||||||
|
// by default since Firefox 103 (2). With storage partitioning, the embedded
|
||||||
|
// site gets a separate storage bucket scoped by the top-level origin, so
|
||||||
|
// the site generally works as expected
|
||||||
|
//
|
||||||
|
// * Brave also implements storage partitioning (3)
|
||||||
|
//
|
||||||
|
// To detect whether cookies are allowed, we check for the user data cookie,
|
||||||
|
// which should be set by the server on the initial request and is accessible
|
||||||
|
// from JavaScript (without HttpOnly).
|
||||||
|
//
|
||||||
|
// Also see the proposal (4), which may streamline this in the future.
|
||||||
|
//
|
||||||
|
// (1): https://developer.mozilla.org/en-US/docs/Web/Privacy/State_Partitioning#state_partitioning
|
||||||
|
// (2): https://www.mozilla.org/en-US/firefox/103.0/releasenotes
|
||||||
|
// (3): https://brave.com/privacy-updates/7-ephemeral-storage
|
||||||
|
// (4): https://github.com/privacycg/CHIPS
|
||||||
|
|
||||||
if (document.hasStorageAccess) {
|
if (loadUserData() === null) {
|
||||||
document.hasStorageAccess().then((hasStorageAccess) => {
|
|
||||||
if (hasStorageAccess) {
|
|
||||||
connect();
|
|
||||||
} else {
|
|
||||||
const overlayEl = document.createElement("div");
|
const overlayEl = document.createElement("div");
|
||||||
|
|
||||||
overlayEl.innerHTML = `
|
overlayEl.innerHTML = `
|
||||||
|
@ -84,7 +106,8 @@ if (document.hasStorageAccess) {
|
||||||
<div class="mt-3 text-sm text-gray-300">
|
<div class="mt-3 text-sm text-gray-300">
|
||||||
It looks like Livebook does not have access to cookies. This usually happens when
|
It looks like Livebook does not have access to cookies. This usually happens when
|
||||||
it runs in an iframe. To make sure the app is fully functional open it in a new
|
it runs in an iframe. To make sure the app is fully functional open it in a new
|
||||||
tab directly.
|
tab directly. Alternatively you can relax security settings for this page to allow
|
||||||
|
third-party cookies.
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-6">
|
<div class="mt-6">
|
||||||
<a id="open-app" class="button-base button-blue" target="_blank">
|
<a id="open-app" class="button-base button-blue" target="_blank">
|
||||||
|
@ -98,8 +121,6 @@ if (document.hasStorageAccess) {
|
||||||
overlayEl.querySelector("#open-app").href = window.location;
|
overlayEl.querySelector("#open-app").href = window.location;
|
||||||
|
|
||||||
document.body.appendChild(overlayEl);
|
document.body.appendChild(overlayEl);
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
connect();
|
connect();
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,14 @@ function getCookieValue(key) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function setCookie(key, value, maxAge) {
|
function setCookie(key, value, maxAge) {
|
||||||
const cookie = `${key}=${value};max-age=${maxAge};path=/`;
|
const cookie = `${key}=${value};max-age=${maxAge};path=/${cookieOptions()}`;
|
||||||
document.cookie = cookie;
|
document.cookie = cookie;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function cookieOptions() {
|
||||||
|
if (document.body.hasAttribute("data-within-iframe")) {
|
||||||
|
return ";SameSite=None;Secure";
|
||||||
|
} else {
|
||||||
|
return ";SameSite=Lax";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
<body
|
<body
|
||||||
class="bg-white"
|
class="bg-white"
|
||||||
data-feature-flags={Livebook.Config.enabled_feature_flags() |> Enum.join(",")}
|
data-feature-flags={Livebook.Config.enabled_feature_flags() |> Enum.join(",")}
|
||||||
|
data-within-iframe={Livebook.Config.within_iframe?()}
|
||||||
>
|
>
|
||||||
<%= @inner_content %>
|
<%= @inner_content %>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -45,9 +45,23 @@ defmodule LivebookWeb.UserPlug do
|
||||||
else
|
else
|
||||||
user_data = user_data(User.new())
|
user_data = user_data(User.new())
|
||||||
encoded = user_data |> Jason.encode!() |> Base.encode64()
|
encoded = user_data |> Jason.encode!() |> Base.encode64()
|
||||||
# Set `http_only` to `false`, so that it can be accessed on the client
|
|
||||||
# Set expiration in 5 years
|
put_resp_cookie(
|
||||||
put_resp_cookie(conn, "lb:user_data", encoded, http_only: false, max_age: 157_680_000)
|
conn,
|
||||||
|
"lb:user_data",
|
||||||
|
encoded,
|
||||||
|
# We disable HttpOnly, so that it can be accessed on the client
|
||||||
|
# and set expiration to 5 years
|
||||||
|
[http_only: false, max_age: 157_680_000] ++ cookie_options()
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp cookie_options() do
|
||||||
|
if Livebook.Config.within_iframe?() do
|
||||||
|
[same_site: "None", secure: true]
|
||||||
|
else
|
||||||
|
[same_site: "Lax"]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue