From a487dfe004e290850225fb5e959856d05be901c5 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Tue, 18 Nov 2025 21:10:02 +0100 Subject: [PATCH] :sparkles: Add better approach for cookie token decoding Remove unnecesary decoding for old tokens and add key identifier and versioning to cookie tokens for handle future changes. --- backend/src/app/http/middleware.clj | 22 +++++++++++++++--- backend/src/app/http/session.clj | 36 +++++++++++++++-------------- backend/src/app/tokens.clj | 26 +++++++++++++-------- 3 files changed, 54 insertions(+), 30 deletions(-) diff --git a/backend/src/app/http/middleware.clj b/backend/src/app/http/middleware.clj index 88f3958289..648950c30b 100644 --- a/backend/src/app/http/middleware.clj +++ b/backend/src/app/http/middleware.clj @@ -14,6 +14,7 @@ [app.config :as cf] [app.http :as-alias http] [app.http.errors :as errors] + [app.tokens :as tokens] [app.util.pointer-map :as pmap] [cuerdas.core :as str] [yetti.adapter :as yt] @@ -272,9 +273,24 @@ process-request (fn [request] (if-let [{:keys [type token] :as auth} (get-token request)] - (if-let [decode-fn (get decoders type)] - (assoc request ::http/auth-data (assoc auth :claims (decode-fn token))) - (assoc request ::http/auth-data auth)) + (let [decode-fn (get decoders type)] + (if (= type :cookie) + (let [metadata (tokens/decode-header token)] + ;; NOTE: we only proceed to decode claims on new + ;; cookie tokens. The old cookies dont need to be + ;; decoded because they use the token string as ID + (if (and (= (:kid metadata) 1) + (= (:ver metadata) 1) + (some? decode-fn)) + (assoc request ::http/auth-data (assoc auth + :claims (decode-fn token) + :metadata metadata)) + (assoc request ::http/auth-data (assoc auth :metadata {:ver 0})))) + + (if decode-fn + (assoc request ::http/auth-data (assoc auth :claims (decode-fn token))) + (assoc request ::http/auth-data auth)))) + request))] (fn [request] diff --git a/backend/src/app/http/session.clj b/backend/src/app/http/session.clj index 0761e0a18a..95548a6e3c 100644 --- a/backend/src/app/http/session.clj +++ b/backend/src/app/http/session.clj @@ -158,14 +158,15 @@ (defn- assign-token [cfg session] - (let [token (tokens/generate cfg - {:iss "authentication" - :aud "penpot" - :sid (:id session) - :iat (:modified-at session) - :uid (:profile-id session) - :sso-provider-id (:sso-provider-id session) - :sso-session-id (:sso-session-id session)})] + (let [claims {:iss "authentication" + :aud "penpot" + :sid (:id session) + :iat (:modified-at session) + :uid (:profile-id session) + :sso-provider-id (:sso-provider-id session) + :sso-session-id (:sso-session-id session)} + header {:kid 1 :ver 1} + token (tokens/generate cfg claims header)] (assoc session :token token))) (defn create-fn @@ -225,13 +226,14 @@ [handler {:keys [::manager] :as cfg}] (assert (manager? manager) "expected valid session manager") (fn [request] - (let [{:keys [type token claims]} (get request ::http/auth-data)] + (let [{:keys [type token claims metadata]} (get request ::http/auth-data)] (cond (= type :cookie) - (let [session (if-let [sid (:sid claims)] - (read-session manager sid) + (let [session (case (:ver metadata) ;; BACKWARD COMPATIBILITY WITH OLD TOKENS - (read-session manager token)) + 0 (read-session manager token) + 1 (some->> (:sid claims) (read-session manager)) + nil) request (cond-> request (some? session) @@ -240,7 +242,7 @@ response (handler request)] - (if (renew-session? session) + (if (and session (renew-session? session)) (let [session (->> session (update-session manager) (assign-token cfg))] @@ -248,11 +250,11 @@ response)) (= type :bearer) - (let [session (if-let [sid (:sid claims)] - (read-session manager sid) + (let [session (case (:ver metadata) ;; BACKWARD COMPATIBILITY WITH OLD TOKENS - (read-session manager token)) - + 0 (read-session manager token) + 1 (some->> (:sid claims) (read-session manager)) + nil) request (cond-> request (some? session) (-> (assoc ::profile-id (:profile-id session)) diff --git a/backend/src/app/tokens.clj b/backend/src/app/tokens.clj index d08b8be58f..18b0374000 100644 --- a/backend/src/app/tokens.clj +++ b/backend/src/app/tokens.clj @@ -15,19 +15,25 @@ [buddy.sign.jwe :as jwe])) (defn generate - [{:keys [::setup/props] :as cfg} claims] - (assert (contains? cfg ::setup/props)) + ([cfg claims] (generate cfg claims nil)) + ([{:keys [::setup/props] :as cfg} claims header] + (assert (contains? props :tokens-key) "expect props to have tokens-key") - (let [tokens-key - (get props :tokens-key) + (let [tokens-key + (get props :tokens-key) - payload - (-> claims - (update :iat (fn [v] (or v (ct/now)))) - (d/without-nils) - (t/encode))] + payload + (-> claims + (update :iat (fn [v] (or v (ct/now)))) + (d/without-nils) + (t/encode))] - (jwe/encrypt payload tokens-key {:alg :a256kw :enc :a256gcm}))) + (jwe/encrypt payload tokens-key {:alg :a256kw :enc :a256gcm :header header})))) + +(defn decode-header + [token] + (ex/ignoring + (jwe/decode-header token))) (defn decode [{:keys [::setup/props] :as cfg} token]