Middleware that sets a permanent browser identifier cookie so we can identify logins from new browsers. This is mostly so we can send people 'login from a new device' emails the first time they log in with a new browser. If this cookie is deleted, it's fine; the user will just get an email saying they logged in from a new device next time they log in. | (ns metabase.server.middleware.browser-cookie (:require [java-time.api :as t] [metabase.request.core :as request] [metabase.util.malli :as mu] [metabase.util.malli.schema :as ms] [ring.util.response :as response])) |
(set! *warn-on-reflection* true) | |
(def ^:private browser-id-cookie-name "metabase.DEVICE") | |
This cookie doesn't need to be secure, because it's only used for notification purposes and cannot be used for CSRF as it is not a session cookie. However, we do need to make sure it's persisted/sent as much as possible to prevent superfluous login notification emails when used with full-app embedding, which means setting SameSite=None when possible (over HTTPS) and SameSite=Lax otherwise. (See #18553) | (defn- cookie-options [request] (merge {:http-only true :path "/" ;; Set the cookie to expire 20 years from now. That should be sufficient :expires (t/format :rfc-1123-date-time (t/plus (t/zoned-date-time) (t/years 20)))} (if (request/https? request) {:same-site :none, :secure true} {:same-site :lax}))) |
(mu/defn- add-browser-id-cookie [request response browser-id :- ms/NonBlankString] (response/set-cookie response browser-id-cookie-name browser-id (cookie-options request))) | |
Set a permanent browser identifier cookie if one is not already set. | (defn ensure-browser-id-cookie [handler] (fn [request respond raise] (if-let [browser-id (get-in request [:cookies browser-id-cookie-name :value])] (handler (assoc request :browser-id browser-id) respond raise) (let [browser-id (str (random-uuid))] (handler (assoc request :browser-id browser-id) (fn [response] (respond (add-browser-id-cookie request response browser-id))) raise))))) |