diff --git a/assets/js/app.js b/assets/js/app.js index b01999ae7..5cb771acc 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -17,6 +17,7 @@ import { loadUserData } from "./lib/user"; import { loadAppAuthToken } from "./lib/app"; import { settingsStore } from "./lib/settings"; import { registerTopbar, registerGlobalEventHandlers } from "./events"; +import { cookieOptions } from "./lib/utils"; function connect() { const csrfToken = document @@ -83,9 +84,8 @@ function connect() { // // * 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). +// To detect whether cookies are allowed, we check if we can programmatically +// set a cookie. // // Also see the proposal (4), which may streamline this in the future. // @@ -94,7 +94,9 @@ function connect() { // (3): https://brave.com/privacy-updates/7-ephemeral-storage // (4): https://github.com/privacycg/CHIPS -if (loadUserData() === null) { +if (hasCookiesAccess()) { + connect(); +} else { const overlayEl = document.createElement("div"); overlayEl.innerHTML = ` @@ -121,6 +123,12 @@ if (loadUserData() === null) { overlayEl.querySelector("#open-app").href = window.location; document.body.appendChild(overlayEl); -} else { - connect(); +} + +function hasCookiesAccess() { + document.cookie = `lb:probe_cookie=;path=/${cookieOptions()}`; + + return document.cookie + .split("; ") + .some((cookie) => cookie.startsWith(`lb:probe_cookie=`)); } diff --git a/assets/js/lib/user.js b/assets/js/lib/user.js index 7929a2b82..ca7dcecac 100644 --- a/assets/js/lib/user.js +++ b/assets/js/lib/user.js @@ -1,4 +1,4 @@ -import { decodeBase64, encodeBase64 } from "./utils"; +import { cookieOptions, decodeBase64, encodeBase64 } from "./utils"; const USER_DATA_COOKIE = "lb:user_data"; @@ -41,11 +41,3 @@ function setCookie(key, value, maxAge) { const cookie = `${key}=${value};max-age=${maxAge};path=/${cookieOptions()}`; document.cookie = cookie; } - -function cookieOptions() { - if (document.body.hasAttribute("data-within-iframe")) { - return ";SameSite=None;Secure"; - } else { - return ";SameSite=Lax"; - } -} diff --git a/assets/js/lib/utils.js b/assets/js/lib/utils.js index 213c40f09..295b98ea9 100644 --- a/assets/js/lib/utils.js +++ b/assets/js/lib/utils.js @@ -235,3 +235,11 @@ export function isFeatureFlagEnabled(feature) { return features.split(",").includes(feature); } } + +export function cookieOptions() { + if (document.body.hasAttribute("data-within-iframe")) { + return ";SameSite=None;Secure"; + } else { + return ";SameSite=Lax"; + } +} diff --git a/lib/livebook_web/endpoint.ex b/lib/livebook_web/endpoint.ex index c3a8e8ac1..260cfeb44 100644 --- a/lib/livebook_web/endpoint.ex +++ b/lib/livebook_web/endpoint.ex @@ -104,6 +104,14 @@ defmodule LivebookWeb.Endpoint do end end + def cookie_options() do + if Livebook.Config.within_iframe?() do + [same_site: "None", secure: true] + else + [same_site: "Lax"] + end + end + # Because we run on localhost, we may accumulate # cookies from several other apps. Our header limit # is set to 32kB. Once we are 75% of said limit, diff --git a/lib/livebook_web/plugs/user_plug.ex b/lib/livebook_web/plugs/user_plug.ex index c764dcd34..3c7093aba 100644 --- a/lib/livebook_web/plugs/user_plug.ex +++ b/lib/livebook_web/plugs/user_plug.ex @@ -46,22 +46,10 @@ defmodule LivebookWeb.UserPlug do user_data = user_data(User.new()) encoded = user_data |> Jason.encode!() |> Base.encode64() - put_resp_cookie( - 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"] + # We disable HttpOnly, so that it can be accessed on the client + # and set expiration to 5 years + opts = [http_only: false, max_age: 157_680_000] ++ LivebookWeb.Endpoint.cookie_options() + put_resp_cookie(conn, "lb:user_data", encoded, opts) end end