user.cljs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. (ns frontend.handler.user
  2. "Provides user related handler fns like login and logout"
  3. (:require [frontend.config :as config]
  4. [frontend.handler.config :as config-handler]
  5. [frontend.state :as state]
  6. [frontend.debug :as debug]
  7. [clojure.string :as string]
  8. [cljs-time.core :as t]
  9. [cljs-time.coerce :as tc]
  10. [cljs-http.client :as http]
  11. [cljs.core.async :as async :refer [go go-loop <! timeout]]))
  12. (defn set-preferred-format!
  13. [format]
  14. (when format
  15. (config-handler/set-config! :preferred-format format)
  16. (state/set-preferred-format! format)))
  17. (defn set-preferred-workflow!
  18. [workflow]
  19. (when workflow
  20. (config-handler/set-config! :preferred-workflow workflow)
  21. (state/set-preferred-workflow! workflow)))
  22. ;;; userinfo, token, login/logout, ...
  23. (defn- parse-jwt [jwt]
  24. (some-> jwt
  25. (string/split ".")
  26. second
  27. js/atob
  28. js/JSON.parse
  29. (js->clj :keywordize-keys true)))
  30. (defn- expired? [parsed-jwt]
  31. (some->
  32. (* 1000 (:exp parsed-jwt))
  33. tc/from-long
  34. (t/before? (t/now))))
  35. (defn- almost-expired?
  36. "return true when jwt will expire after 1h"
  37. [parsed-jwt]
  38. (some->
  39. (* 1000 (:exp parsed-jwt))
  40. tc/from-long
  41. (t/before? (-> 1 t/hours t/from-now))))
  42. (defn email []
  43. (some->
  44. (state/get-auth-id-token)
  45. parse-jwt
  46. :email))
  47. (defn user-uuid []
  48. (some->
  49. (state/get-auth-id-token)
  50. parse-jwt
  51. :sub))
  52. (defn logged-in? []
  53. (boolean
  54. (some->
  55. (state/get-auth-id-token)
  56. parse-jwt
  57. expired?
  58. not)))
  59. (defn- set-token-to-localstorage!
  60. ([id-token access-token]
  61. (prn :debug "set-token-to-localstorage!")
  62. (js/localStorage.setItem "id-token" id-token)
  63. (js/localStorage.setItem "access-token" access-token))
  64. ([id-token access-token refresh-token]
  65. (prn :debug "set-token-to-localstorage!")
  66. (js/localStorage.setItem "id-token" id-token)
  67. (js/localStorage.setItem "access-token" access-token)
  68. (js/localStorage.setItem "refresh-token" refresh-token)))
  69. (defn- clear-tokens
  70. []
  71. (state/set-auth-id-token nil)
  72. (state/set-auth-access-token nil)
  73. (state/set-auth-refresh-token nil)
  74. (set-token-to-localstorage! "" "" ""))
  75. (defn- set-tokens!
  76. ([id-token access-token]
  77. (state/set-auth-id-token id-token)
  78. (state/set-auth-access-token access-token)
  79. (set-token-to-localstorage! id-token access-token))
  80. ([id-token access-token refresh-token]
  81. (state/set-auth-id-token id-token)
  82. (state/set-auth-access-token access-token)
  83. (state/set-auth-refresh-token refresh-token)
  84. (set-token-to-localstorage! id-token access-token refresh-token)))
  85. (defn <refresh-id-token&access-token
  86. "refresh id-token and access-token, if refresh_token expired, clear all tokens
  87. return true if success, else false"
  88. []
  89. (go
  90. (when-let [refresh-token (state/get-auth-refresh-token)]
  91. (let [resp (<! (http/get (str "https://" config/API-DOMAIN "/auth_refresh_token?refresh_token=" refresh-token)
  92. {:with-credentials? false}))]
  93. (cond
  94. ;; e.g. api return 500, server internal error
  95. ;; we shouldn't clear tokens if they aren't expired yet
  96. ;; the `refresh-tokens-loop` will retry soon
  97. (and (not (http/unexceptional-status? (:status resp)))
  98. (not (-> (state/get-auth-id-token) parse-jwt expired?)))
  99. nil ; do nothing
  100. (not (http/unexceptional-status? (:status resp)))
  101. (clear-tokens)
  102. :else ; ok
  103. (set-tokens! (:id_token (:body resp)) (:access_token (:body resp))))))))
  104. (defn restore-tokens-from-localstorage
  105. "restore id-token, access-token, refresh-token from localstorage,
  106. and refresh id-token&access-token if necessary.
  107. return nil when tokens are not available."
  108. []
  109. (println "restore-tokens-from-localstorage")
  110. (let [id-token (js/localStorage.getItem "id-token")
  111. access-token (js/localStorage.getItem "access-token")
  112. refresh-token (js/localStorage.getItem "refresh-token")]
  113. (when refresh-token
  114. (set-tokens! id-token access-token refresh-token)
  115. (when-not (or (nil? id-token) (nil? access-token)
  116. (-> id-token parse-jwt almost-expired?)
  117. (-> access-token parse-jwt almost-expired?))
  118. (go
  119. ;; id-token or access-token expired
  120. (<! (<refresh-id-token&access-token))
  121. ;; refresh remote graph list by pub login event
  122. (when (user-uuid) (state/pub-event! [:user/login])))))))
  123. (defn login-callback [code]
  124. (state/set-state! [:ui/loading? :login] true)
  125. (go
  126. (let [resp (<! (http/get (str "https://" config/API-DOMAIN "/auth_callback?code=" code)
  127. {:with-credentials? false}))]
  128. (if (= 200 (:status resp))
  129. (-> resp
  130. :body
  131. (as-> $ (set-tokens! (:id_token $) (:access_token $) (:refresh_token $)))
  132. (#(state/pub-event! [:user/login])))
  133. (debug/pprint "login-callback" resp)))))
  134. (defn logout []
  135. (clear-tokens)
  136. (state/pub-event! [:user/logout]))
  137. ;;; refresh tokens loop
  138. (def stop-refresh false)
  139. (defn refresh-tokens-loop []
  140. (debug/pprint "start refresh-tokens-loop")
  141. (go-loop []
  142. (<! (timeout 60000))
  143. (when (state/get-auth-refresh-token)
  144. (let [id-token (state/get-auth-id-token)]
  145. (when (or (nil? id-token)
  146. (-> id-token (parse-jwt) (almost-expired?)))
  147. (debug/pprint (str "refresh tokens... " (tc/to-string(t/now))))
  148. (<! (<refresh-id-token&access-token)))))
  149. (when-not stop-refresh
  150. (recur))))
  151. (defn alpha-user?
  152. []
  153. (or config/dev?
  154. (contains? (state/user-groups) "alpha-tester")))
  155. (comment
  156. (defn beta-user?
  157. []
  158. (contains? (state/user-groups) "beta-tester")))