From bb73ddc58fd133d039030cf449a33142f331f276 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Sun, 25 Apr 2021 19:43:09 +0200 Subject: [PATCH] :sparkles: Replace random session tokens with JWE tokens. We still maintain the http session state on the database for to prevent replay attacks to the main application. But internally, on less critical parts of the infraestructure, it usefull have access to the identified user without hit the main database for that information. --- backend/src/app/config.clj | 1 - backend/src/app/http/session.clj | 89 ++++++++++++++++---------------- backend/src/app/main.clj | 4 +- 3 files changed, 46 insertions(+), 48 deletions(-) diff --git a/backend/src/app/config.clj b/backend/src/app/config.clj index 260e00c5f..0e9d4ac5d 100644 --- a/backend/src/app/config.clj +++ b/backend/src/app/config.clj @@ -116,7 +116,6 @@ (s/def ::oidc-roles-attr ::us/keyword) (s/def ::host ::us/string) (s/def ::http-server-port ::us/integer) -(s/def ::http-session-cookie-name ::us/string) (s/def ::http-session-idle-max-age ::dt/duration) (s/def ::http-session-updater-batch-max-age ::dt/duration) (s/def ::http-session-updater-batch-max-size ::us/integer) diff --git a/backend/src/app/http/session.clj b/backend/src/app/http/session.clj index 2f071089d..c03f0995b 100644 --- a/backend/src/app/http/session.clj +++ b/backend/src/app/http/session.clj @@ -21,86 +21,85 @@ [clojure.spec.alpha :as s] [integrant.core :as ig])) +;; A default cookie name for storing the session. We don't allow +;; configure it. +(def cookie-name "session-id") + ;; --- IMPL -(defn- next-session-id - ([] (next-session-id 96)) - ([n] - (-> (bn/random-nonce n) - (bc/bytes->b64u) - (bc/bytes->str)))) +(defn- create-session + [{:keys [conn tokens] :as cfg} {:keys [profile-id headers] :as request}] + (let [token (tokens :generate {:iss "authentication" + :iat (dt/now) + :uid profile-id}) + params {:user-agent (get headers "user-agent") + :profile-id profile-id + :id token}] + (db/insert! conn :http-session params))) -(defn- create - [{:keys [conn] :as cfg} {:keys [profile-id user-agent]}] - (let [id (next-session-id)] - (db/insert! conn :http-session {:id id - :profile-id profile-id - :user-agent user-agent}) - id)) - -(defn- delete - [{:keys [conn cookie-name] :as cfg} {:keys [cookies] :as request}] +(defn- delete-session + [{:keys [conn] :as cfg} {:keys [cookies] :as request}] (when-let [token (get-in cookies [cookie-name :value])] (db/delete! conn :http-session {:id token})) nil) -(defn- retrieve - [{:keys [conn] :as cfg} token] - (when token - (db/exec-one! conn ["select id, profile_id from http_session where id = ?" token]))) +(defn- retrieve-session + [{:keys [conn] :as cfg} id] + (when id + (db/exec-one! conn ["select id, profile_id from http_session where id = ?" id]))) (defn- retrieve-from-request - [{:keys [cookie-name] :as cfg} {:keys [cookies] :as request}] + [cfg {:keys [cookies] :as request}] (->> (get-in cookies [cookie-name :value]) - (retrieve cfg))) + (retrieve-session cfg))) -(defn- cookies - [{:keys [cookie-name] :as cfg} vals] - {cookie-name (merge vals {:path "/" :http-only true})}) +(defn- add-cookies + [response {:keys [id] :as session}] + (assoc response :cookies {"session-id" {:path "/" :http-only true :value id}})) + +(defn- clear-cookies + [response] + (assoc response :cookies {"session-id" {:value "" :max-age -1}})) (defn- middleware [cfg handler] (fn [request] (if-let [{:keys [id profile-id] :as session} (retrieve-from-request cfg request)] - (let [events-ch (::events-ch cfg)] - (a/>!! events-ch id) + (do + (a/>!! (::events-ch cfg) id) (l/update-thread-context! {:profile-id profile-id}) (handler (assoc request :profile-id profile-id))) (handler request)))) ;; --- STATE INIT: SESSION -(s/def ::cookie-name ::cfg/http-session-cookie-name) - (defmethod ig/pre-init-spec ::session [_] - (s/keys :req-un [::db/pool] - :opt-un [::cookie-name])) + (s/keys :req-un [::db/pool])) (defmethod ig/prep-key ::session [_ cfg] - (merge {:cookie-name "auth-token" - :buffer-size 64} - (d/without-nils cfg))) + (d/merge {:buffer-size 64} + (d/without-nils cfg))) (defmethod ig/init-key ::session [_ {:keys [pool] :as cfg}] (let [events (a/chan (a/dropping-buffer (:buffer-size cfg))) - cfg (assoc cfg - :conn pool - ::events-ch events)] + cfg (-> cfg + (assoc :conn pool) + (assoc ::events-ch events))] (-> cfg (assoc :middleware #(middleware cfg %)) (assoc :create (fn [profile-id] (fn [request response] - (let [uagent (get-in request [:headers "user-agent"]) - value (create cfg {:profile-id profile-id :user-agent uagent})] - (assoc response :cookies (cookies cfg {:value value})))))) + (let [request (assoc request :profile-id profile-id) + session (create-session cfg request)] + (add-cookies response session))))) (assoc :delete (fn [request response] - (delete cfg request) - (assoc response - :status 204 - :body "" - :cookies (cookies cfg {:value "" :max-age -1}))))))) + (delete-session cfg request) + (-> response + (assoc :status 204) + (assoc :body "") + (clear-cookies))))))) (defmethod ig/halt-key! ::session [_ data] diff --git a/backend/src/app/main.clj b/backend/src/app/main.clj index e40946e14..ab8c96ebb 100644 --- a/backend/src/app/main.clj +++ b/backend/src/app/main.clj @@ -60,8 +60,8 @@ :storage (ig/ref :app.storage/storage)} :app.http.session/session - {:pool (ig/ref :app.db/pool) - :cookie-name (cf/get :http-session-cookie-name)} + {:pool (ig/ref :app.db/pool) + :tokens (ig/ref :app.tokens/tokens)} :app.http.session/gc-task {:pool (ig/ref :app.db/pool)