From f12f8e14c247d6295f0419540c7911a0b796b31c Mon Sep 17 00:00:00 2001 From: Bryce Date: Wed, 10 Apr 2024 00:36:41 -0700 Subject: [PATCH] Forces users to re login when there's a major update --- src/clj/auto_ap/handler.clj | 10 +++++--- src/clj/auto_ap/routes/auth.clj | 6 +++-- src/clj/auto_ap/session_version.clj | 36 +++++++++++++++++++++++++++++ src/clj/auto_ap/ssr/auth.clj | 6 +++-- src/cljs/auto_ap/effects.cljs | 10 ++++---- src/cljs/auto_ap/events.cljs | 10 ++++++-- 6 files changed, 64 insertions(+), 14 deletions(-) create mode 100644 src/clj/auto_ap/session_version.clj diff --git a/src/clj/auto_ap/handler.clj b/src/clj/auto_ap/handler.clj index ace8ffea..6629c598 100644 --- a/src/clj/auto_ap/handler.clj +++ b/src/clj/auto_ap/handler.clj @@ -2,8 +2,7 @@ (:require [amazonica.core :refer [defcredential]] [auto-ap.client-routes :as client-routes] [auto-ap.datomic :refer [conn pull-many]] - [auto-ap.graphql.utils :refer [assert-can-see-client - limited-clients]] + [auto-ap.graphql.utils :refer [limited-clients]] [auto-ap.logging :as alog] [auto-ap.routes.auth :as auth] [auto-ap.routes.exports :as exports] @@ -13,6 +12,7 @@ [auto-ap.routes.invoices :as invoices] [auto-ap.routes.queries :as queries] [auto-ap.routes.yodlee2 :as yodlee2] + [auto-ap.session-version :as session-version] [auto-ap.ssr-routes :as ssr-routes] [auto-ap.ssr.core :as ssr] [auto-ap.ssr.utils :refer [entity-id main-transformer]] @@ -160,10 +160,12 @@ :exception e) (throw e))))))) + + (defn wrap-idle-session-timeout [handler] (fn [request] - (let [session (:session request {}) + (let [session (:session request {:version session-version/current-session-version}) end-time (coerce/to-date-time (::idle-timeout session))] (if (and end-time (time/before? end-time (time/now))) (if (get (:headers request) "hx-request") @@ -317,6 +319,8 @@ (dissoc auth :exp))})) #_(wrap-pprint-session) + + (session-version/wrap-session-version) (wrap-idle-session-timeout) (wrap-session {:store (cookie-store {:key diff --git a/src/clj/auto_ap/routes/auth.clj b/src/clj/auto_ap/routes/auth.clj index 9925f338..517e12f0 100644 --- a/src/clj/auto_ap/routes/auth.clj +++ b/src/clj/auto_ap/routes/auth.clj @@ -8,7 +8,8 @@ [config.core :refer [env]] [com.brunobonacci.mulog :as mu] [clojure.java.io :as io] - [clojure.edn :as edn])) + [clojure.edn :as edn] + [auto-ap.session-version :as session-version])) (def google-client-id "264081895820-0nndcfo3pbtqf30sro82vgq5r27h8736.apps.googleusercontent.com") (def google-client-secret "OC-WemHurPXYpuIw5cT-B90g") @@ -94,7 +95,8 @@ (jwt/sign jwt (:jwt-secret env) {:alg :hs512}))} - :session {:identity (dissoc jwt :exp)}} + :session {:identity (dissoc jwt :exp) + :version session-version/current-session-version}} {:status 401 :body "Couldn't authenticate"})) (catch Exception e diff --git a/src/clj/auto_ap/session_version.clj b/src/clj/auto_ap/session_version.clj new file mode 100644 index 00000000..f623c3fa --- /dev/null +++ b/src/clj/auto_ap/session_version.clj @@ -0,0 +1,36 @@ +(ns auto-ap.session-version + (:require [bidi.bidi :as bidi])) + +;; TODO this should only be done until SSR is complete +;; once it is, it should just use redirects based on headers +;; no header=use default, mismatch header=redirect to login +(def current-session-version 1) +(defn wrap-session-version + [handler] + (fn [request] + (let [session (:session request) + route (bidi/match-route @(resolve 'auto-ap.handler/all-routes) + (:uri request) + :request-method (:request-method request)) + is-normal-route? (or (keyword? route) + (keyword? (:handler route)))] ;; TODO SSR icky + (if (and (not= (:version session) current-session-version) + (not= :login route) + (not= :oauth route) + (not= :oauth (:handler route)) + (not= :login (:handler route)) + is-normal-route?) + (cond + (or (= :graphql (:handler route)) + (= :graphql route)) + {:status 401} + + (get (:headers request) "hx-request") + {:session nil + :status 200 + :headers {"hx-redirect" "/login"}} + :else + {:session nil + :status 302 + :headers {"Location" "/login"}}) + (handler request))))) \ No newline at end of file diff --git a/src/clj/auto_ap/ssr/auth.clj b/src/clj/auto_ap/ssr/auth.clj index 6f863dc4..84fde14a 100644 --- a/src/clj/auto_ap/ssr/auth.clj +++ b/src/clj/auto_ap/ssr/auth.clj @@ -1,5 +1,6 @@ (ns auto-ap.ssr.auth - (:require [buddy.sign.jwt :as jwt] + (:require [auto-ap.session-version :as session-version] + [buddy.sign.jwt :as jwt] [config.core :refer [env]])) (defn logout [request] @@ -13,4 +14,5 @@ :session {:identity (dissoc (jwt/unsign (get-in request [:query-params "jwt"]) (:jwt-secret env) {:alg :hs512}) - :exp)}}) + :exp) + :version session-version/current-session-version}}) diff --git a/src/cljs/auto_ap/effects.cljs b/src/cljs/auto_ap/effects.cljs index 5f0ae121..293e0f71 100644 --- a/src/cljs/auto_ap/effects.cljs +++ b/src/cljs/auto_ap/effects.cljs @@ -197,27 +197,27 @@ "&variables=" (pr-str (or variables {})))}))] (cond + (= (:status response) 401) (re-frame/dispatch [:auto-ap.events/logout "Your session has expired. Please log in again."]) - + (>= (:status response) 400) (let [error (->> response :body :errors (dates->date-times) - (map #(assoc % :status (:status response))) - )] + (map #(assoc % :status (:status response))))] (when (:multi owns-state) (re-frame/dispatch [::status/error-multi (:multi owns-state) (:which owns-state) error])) (when (:single owns-state) (re-frame/dispatch [::status/error (:single owns-state) error])) (when on-error - (->> error + (->> error (conj on-error) (re-frame/dispatch)))) :else - (do + (do (when (:multi owns-state) (re-frame/dispatch [::status/completed-multi (:multi owns-state) (:which owns-state)])) (when (:single owns-state) diff --git a/src/cljs/auto_ap/events.cljs b/src/cljs/auto_ap/events.cljs index d85f0291..20107835 100644 --- a/src/cljs/auto_ap/events.cljs +++ b/src/cljs/auto_ap/events.cljs @@ -60,7 +60,13 @@ (fn [{:keys [_]} [_ token]] (let [handler (:handler (bidi/match-route routes/routes (.. js/window -location -pathname))) last-client-id (.getItem js/localStorage "last-client-id") - last-selected-clients (js->clj (.parse js/JSON (.getItem js/localStorage "last-selected-clients"))) + last-selected-clients (try (some->> "last-selected-clients" + (.getItem js/localStorage) + not-empty + (.parse js/JSON) + js->clj) + (catch js/Error e + :all)) last-selected-clients (mc/decode client-selection-schema last-selected-clients client-selection-transformer) jwt-data (some-> token jwt->data) selected-client-assignment (cond (and token @@ -74,7 +80,7 @@ [(js/parseInt last-client-id)] :else - nil) ] + nil)] (cond