mirror of
https://github.com/logseq/logseq.git
synced 2026-05-18 01:42:19 +00:00
fix(sync): refresh access token when expired
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
[clojure.set :as set]
|
||||
[clojure.string :as string]
|
||||
[frontend.common.missionary :as c.m]
|
||||
[frontend.common.thread-api :refer [def-thread-api]]
|
||||
[frontend.config :as config]
|
||||
[frontend.debug :as debug]
|
||||
[frontend.flows :as flows]
|
||||
@@ -17,7 +18,8 @@
|
||||
[goog.crypt.Hmac]
|
||||
[goog.crypt.Sha256]
|
||||
[goog.crypt.base64 :as base64]
|
||||
[missionary.core :as m]))
|
||||
[missionary.core :as m]
|
||||
[promesa.core :as p]))
|
||||
|
||||
;;; userinfo, token, login/logout, ...
|
||||
|
||||
@@ -301,6 +303,11 @@
|
||||
(-> (state/get-auth-id-token) parse-jwt expired?))
|
||||
(throw (ex-info "empty or expired token and refresh failed" {:type :expired-token})))))))
|
||||
|
||||
(def-thread-api :thread-api/ensure-id&access-token
|
||||
[]
|
||||
(p/let [_ (js/Promise. task--ensure-id&access-token)]
|
||||
{:id-token (state/get-auth-id-token)}))
|
||||
|
||||
;;; user groups
|
||||
|
||||
(defn rtc-group?
|
||||
|
||||
@@ -134,6 +134,28 @@
|
||||
(defn- auth-token []
|
||||
(worker-state/get-id-token))
|
||||
|
||||
(defn- id-token-expired?
|
||||
[token]
|
||||
(if-not (string? token)
|
||||
true
|
||||
(try
|
||||
(let [exp-ms (some-> token worker-util/parse-jwt :exp (* 1000))]
|
||||
(or (not (number? exp-ms))
|
||||
(<= exp-ms (common-util/time-ms))))
|
||||
(catch :default _
|
||||
true))))
|
||||
|
||||
(defn- <resolve-ws-token
|
||||
[]
|
||||
(let [token (auth-token)]
|
||||
(if (id-token-expired? token)
|
||||
(p/let [resp (worker-state/<invoke-main-thread :thread-api/ensure-id&access-token)
|
||||
refreshed-token (:id-token resp)]
|
||||
(when (string? refreshed-token)
|
||||
(worker-state/set-new-state! {:auth/id-token refreshed-token})
|
||||
refreshed-token))
|
||||
(p/resolved token))))
|
||||
|
||||
(defn- get-user-uuid []
|
||||
(some-> (worker-state/get-id-token)
|
||||
worker-util/parse-jwt
|
||||
@@ -1376,8 +1398,12 @@
|
||||
(when-let [current @worker-state/*db-sync-client]
|
||||
(when (and (= (:repo current) repo)
|
||||
(= (:graph-id current) (:graph-id client)))
|
||||
(let [updated (connect! repo current url)]
|
||||
(reset! worker-state/*db-sync-client updated)))))
|
||||
(-> (p/let [token (<resolve-ws-token)
|
||||
updated (connect! repo current url token)]
|
||||
(reset! worker-state/*db-sync-client updated))
|
||||
(p/catch (fn [error]
|
||||
(log/error :db-sync/ws-reconnect-failed {:repo repo :error error})
|
||||
(schedule-reconnect! repo current url :connect-failed)))))))
|
||||
delay)]
|
||||
(swap! reconnect assoc :timer timeout-id :attempt (inc attempt))
|
||||
(log/info :db-sync/ws-reconnect-scheduled
|
||||
@@ -1443,20 +1469,21 @@
|
||||
(catch :default _
|
||||
nil))))
|
||||
|
||||
(defn- connect! [repo client url]
|
||||
(defn- connect! [repo client url token]
|
||||
(when (:ws client)
|
||||
(stop-client! client))
|
||||
(let [ws (js/WebSocket. (append-token url (auth-token)))
|
||||
updated (assoc client :ws ws)]
|
||||
(attach-ws-handlers! repo updated ws url)
|
||||
(set! (.-onopen ws)
|
||||
(fn [_]
|
||||
(reset-reconnect! updated)
|
||||
(touch-last-ws-message! updated)
|
||||
(set-ws-state! updated :open)
|
||||
(send! ws {:type "hello" :client repo})
|
||||
(enqueue-asset-sync! repo updated)))
|
||||
(close-stale-ws-loop updated ws)))
|
||||
(when token
|
||||
(let [ws (js/WebSocket. (append-token url token))
|
||||
updated (assoc client :ws ws)]
|
||||
(attach-ws-handlers! repo updated ws url)
|
||||
(set! (.-onopen ws)
|
||||
(fn [_]
|
||||
(reset-reconnect! updated)
|
||||
(touch-last-ws-message! updated)
|
||||
(set-ws-state! updated :open)
|
||||
(send! ws {:type "hello" :client repo})
|
||||
(enqueue-asset-sync! repo updated)))
|
||||
(close-stale-ws-loop updated ws))))
|
||||
|
||||
(defn stop!
|
||||
[]
|
||||
@@ -1505,7 +1532,8 @@
|
||||
url (format-ws-url base graph-id)
|
||||
_ (ensure-client-graph-uuid! repo graph-id)
|
||||
connected (assoc client :graph-id graph-id)
|
||||
connected (connect! repo connected url)]
|
||||
token (<resolve-ws-token)
|
||||
connected (connect! repo connected url token)]
|
||||
(reset! worker-state/*db-sync-client connected)
|
||||
nil))
|
||||
(p/finally
|
||||
|
||||
@@ -61,6 +61,78 @@
|
||||
:child2 child2
|
||||
:child3 child3}))
|
||||
|
||||
(deftest resolve-ws-token-refreshes-when-token-expired-test
|
||||
(async done
|
||||
(let [refresh-calls (atom 0)
|
||||
main-thread-prev @worker-state/*main-thread
|
||||
worker-state-prev @worker-state/*state]
|
||||
(reset! worker-state/*state (assoc worker-state-prev :auth/id-token "expired-token"))
|
||||
(reset! worker-state/*main-thread
|
||||
(fn [qkw _direct-pass? _args-list]
|
||||
(if (= qkw :thread-api/ensure-id&access-token)
|
||||
(do
|
||||
(swap! refresh-calls inc)
|
||||
(p/resolved {:id-token "fresh-token"}))
|
||||
(p/resolved nil))))
|
||||
(with-redefs [db-sync/auth-token (fn [] "expired-token")
|
||||
db-sync/id-token-expired? (fn [_token] true)]
|
||||
(-> (#'db-sync/<resolve-ws-token)
|
||||
(p/then (fn [token]
|
||||
(is (= 1 @refresh-calls))
|
||||
(is (= "fresh-token" token))
|
||||
(is (= "fresh-token" (worker-state/get-id-token)))
|
||||
(reset! worker-state/*main-thread main-thread-prev)
|
||||
(reset! worker-state/*state worker-state-prev)
|
||||
(done)))
|
||||
(p/catch (fn [error]
|
||||
(reset! worker-state/*main-thread main-thread-prev)
|
||||
(reset! worker-state/*state worker-state-prev)
|
||||
(is nil (str error))
|
||||
(done))))))))
|
||||
|
||||
(deftest resolve-ws-token-skips-refresh-when-token-not-expired-test
|
||||
(async done
|
||||
(let [refresh-calls (atom 0)
|
||||
main-thread-prev @worker-state/*main-thread]
|
||||
(reset! worker-state/*main-thread
|
||||
(fn [qkw _direct-pass? _args-list]
|
||||
(when (= qkw :thread-api/ensure-id&access-token)
|
||||
(swap! refresh-calls inc))
|
||||
(p/resolved {:id-token "fresh-token"})))
|
||||
(with-redefs [db-sync/auth-token (fn [] "valid-token")
|
||||
db-sync/id-token-expired? (fn [_token] false)]
|
||||
(-> (#'db-sync/<resolve-ws-token)
|
||||
(p/then (fn [token]
|
||||
(reset! worker-state/*main-thread main-thread-prev)
|
||||
(is (= 0 @refresh-calls))
|
||||
(is (= "valid-token" token))
|
||||
(done)))
|
||||
(p/catch (fn [error]
|
||||
(reset! worker-state/*main-thread main-thread-prev)
|
||||
(is nil (str error))
|
||||
(done))))))))
|
||||
|
||||
(deftest resolve-ws-token-refreshes-when-token-missing-test
|
||||
(async done
|
||||
(let [refresh-calls (atom 0)
|
||||
main-thread-prev @worker-state/*main-thread]
|
||||
(reset! worker-state/*main-thread
|
||||
(fn [qkw _direct-pass? _args-list]
|
||||
(when (= qkw :thread-api/ensure-id&access-token)
|
||||
(swap! refresh-calls inc))
|
||||
(p/resolved {:id-token "fresh-token"})))
|
||||
(with-redefs [db-sync/auth-token (fn [] nil)]
|
||||
(-> (#'db-sync/<resolve-ws-token)
|
||||
(p/then (fn [token]
|
||||
(reset! worker-state/*main-thread main-thread-prev)
|
||||
(is (= 1 @refresh-calls))
|
||||
(is (= "fresh-token" token))
|
||||
(done)))
|
||||
(p/catch (fn [error]
|
||||
(reset! worker-state/*main-thread main-thread-prev)
|
||||
(is nil (str error))
|
||||
(done))))))))
|
||||
|
||||
(deftest update-online-users-dedupes-identical-messages-test
|
||||
(let [client {:repo test-repo
|
||||
:online-users (atom [])
|
||||
|
||||
Reference in New Issue
Block a user