0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-02-20 13:55:34 -05:00

♻️ Minor refactor of auth data-flow.

This fixes many issues related to using penpot on-premise
instances on different domain than localhost. This changes
ensures correct data flow of authenticated and not authenticated
sessions.
This commit is contained in:
Andrey Antukh 2021-11-26 09:08:23 +01:00 committed by Alonso Torres
parent ece914303a
commit 2596ad27c3
10 changed files with 95 additions and 56 deletions

View file

@ -58,7 +58,9 @@
(assoc response :cookies {cookie-name {:path "/" (assoc response :cookies {cookie-name {:path "/"
:http-only true :http-only true
:value id :value id
:same-site (if cors? :none :strict) :same-site (cond (not secure?) :lax
cors? :none
:else :strict)
:secure secure?}}))) :secure secure?}})))
(defn- clear-cookies (defn- clear-cookies

View file

@ -335,9 +335,9 @@
;; --- MUTATION: Logout ;; --- MUTATION: Logout
(s/def ::logout (s/def ::logout
(s/keys :req-un [::profile-id])) (s/keys :opt-un [::profile-id]))
(sv/defmethod ::logout (sv/defmethod ::logout {:auth false}
[{:keys [session] :as cfg} _] [{:keys [session] :as cfg} _]
(with-meta {} (with-meta {}
{:transform-response (:delete session)})) {:transform-response (:delete session)}))

View file

@ -7,6 +7,7 @@
(ns app.main.data.users (ns app.main.data.users
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.exceptions :as ex]
[app.common.spec :as us] [app.common.spec :as us]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.config :as cf] [app.config :as cf]
@ -90,6 +91,8 @@
;; --- EVENT: fetch-profile ;; --- EVENT: fetch-profile
(declare logout)
(def profile-fetched? (def profile-fetched?
(ptk/type? ::profile-fetched)) (ptk/type? ::profile-fetched))
@ -102,18 +105,18 @@
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(-> state (cond-> state
(assoc :profile-id id) (is-authenticated? profile)
(assoc :profile profile))) (-> (assoc :profile-id id)
(assoc :profile profile))))
ptk/EffectEvent ptk/EffectEvent
(effect [_ state _] (effect [_ state _]
(let [profile (:profile state)] (when-let [profile (:profile state)]
(when (not= uuid/zero (:id profile))
(swap! storage assoc :profile profile) (swap! storage assoc :profile profile)
(i18n/set-locale! (:lang profile)) (i18n/set-locale! (:lang profile))
(some-> (:theme profile) (some-> (:theme profile)
(theme/set-current-theme!))))))) (theme/set-current-theme!))))))
(defn fetch-profile (defn fetch-profile
[] []
@ -154,38 +157,65 @@
ptk/WatchEvent ptk/WatchEvent
(watch [_ _ _] (watch [_ _ _]
(when (is-authenticated? profile)
(let [team-id (:default-team-id profile)] (let [team-id (:default-team-id profile)]
(->> (rx/concat (->> (rx/of (profile-fetched profile)
(rx/of (profile-fetched profile) (fetch-teams)
(fetch-teams)) (rt/nav' :dashboard-projects {:team-id team-id}))
(->> (rx/of (rt/nav' :dashboard-projects {:team-id team-id})) (rx/observe-on :async)))))))
(rx/delay 1000)))
(rx/observe-on :async))))))
(s/def ::login-params (s/def ::login-params
(s/keys :req-un [::email ::password])) (s/keys :req-un [::email ::password]))
(declare login-from-register)
(defn login (defn login
[{:keys [email password] :as data}] [{:keys [email password] :as data}]
(us/verify ::login-params data) (us/verify ::login-params data)
(ptk/reify ::login (ptk/reify ::login
ptk/WatchEvent ptk/WatchEvent
(watch [_ _ _] (watch [_ _ stream]
(let [{:keys [on-error on-success] (let [{:keys [on-error on-success]
:or {on-error rx/throw :or {on-error rx/throw
on-success identity}} (meta data) on-success identity}} (meta data)
params {:email email params {:email email
:password password :password password
:scope "webapp"}] :scope "webapp"}]
(->> (rx/timer 100)
(rx/mapcat #(rp/mutation :login params)) ;; NOTE: We can't take the profile value from login because
(rx/tap on-success) ;; there are cases when login is successfull but the cookie is
(rx/catch on-error) ;; not set properly (because of possible misconfiguration).
;; So, we proceed to make an additional call to fetch the
;; profile, and ensure that cookie is set correctly. If
;; profile fetch is successful, we mark the user logged in, if
;; the returned profile is an NOT authenticated profile, we
;; proceed to logout and show an error message.
(rx/merge
(->> (rp/mutation :login params)
(rx/map fetch-profile)
(rx/catch on-error))
(->> stream
(rx/filter profile-fetched?)
(rx/take 1)
(rx/map deref)
(rx/filter (complement is-authenticated?))
(rx/tap on-error)
(rx/map #(ex/raise :type :authentication))
(rx/observe-on :async))
(->> stream
(rx/filter profile-fetched?)
(rx/take 1)
(rx/map deref)
(rx/filter is-authenticated?)
(rx/map (fn [profile] (rx/map (fn [profile]
(with-meta profile (with-meta profile
{::ev/source "login"}))) {::ev/source "login"})))
(rx/tap on-success)
(rx/map logged-in) (rx/map logged-in)
(rx/observe-on :async)))) (rx/observe-on :async)))))
ptk/EffectEvent ptk/EffectEvent
(effect [_ _ _] (effect [_ _ _]

View file

@ -13,6 +13,7 @@
[app.main.data.users :as du] [app.main.data.users :as du]
[app.main.sentry :as sentry] [app.main.sentry :as sentry]
[app.main.store :as st] [app.main.store :as st]
[app.util.i18n :refer [tr]]
[app.util.router :as rt] [app.util.router :as rt]
[app.util.timers :as ts] [app.util.timers :as ts]
[cljs.pprint :refer [pprint]] [cljs.pprint :refer [pprint]]
@ -48,7 +49,9 @@
;; here and not in app.main.errors because of circular dependency. ;; here and not in app.main.errors because of circular dependency.
(defmethod ptk/handle-error :authentication (defmethod ptk/handle-error :authentication
[_] [_]
(ts/schedule (st/emitf (du/logout)))) (let [msg (tr "errors.auth.unable-to-login")]
(st/emit! (du/logout))
(ts/schedule 500 (st/emitf (dm/warn msg)))))
;; That are special case server-errors that should be treated ;; That are special case server-errors that should be treated

View file

@ -73,14 +73,14 @@
:dashboard-font-providers :dashboard-font-providers
:dashboard-team-members :dashboard-team-members
:dashboard-team-settings) :dashboard-team-settings)
(when profile
(let [props (:props profile)]
[:* [:*
#_[:div.modal-wrapper #_[:div.modal-wrapper
#_[:& app.main.ui.onboarding/onboarding-templates-modal] #_[:& app.main.ui.onboarding/onboarding-templates-modal]
[:& app.main.ui.onboarding/onboarding-modal] #_[:& app.main.ui.onboarding/onboarding-modal]
#_[:& app.main.ui.onboarding/onboarding-team-modal] #_[:& app.main.ui.onboarding/onboarding-team-modal]
] ]
(when-let [props (some-> profile (get :props {}))]
(cond (cond
(and cf/onboarding-form-id (and cf/onboarding-form-id
(not (:onboarding-questions-answered props false))) (not (:onboarding-questions-answered props false)))
@ -94,8 +94,9 @@
(and (:onboarding-viewed props) (and (:onboarding-viewed props)
(not= (:release-notes-viewed props) (:main @cf/version)) (not= (:release-notes-viewed props) (:main @cf/version))
(not= "0.0" (:main @cf/version))) (not= "0.0" (:main @cf/version)))
[:& app.main.ui.releases/release-notes-modal {}]) [:& app.main.ui.releases/release-notes-modal {}]))
[:& dashboard {:route route}]]))
[:& dashboard {:route route :profile profile}]]
:viewer :viewer
(let [{:keys [query-params path-params]} route (let [{:keys [query-params path-params]} route

View file

@ -74,9 +74,8 @@
nil)]) nil)])
(mf/defc dashboard (mf/defc dashboard
[{:keys [route] :as props}] [{:keys [route profile] :as props}]
(let [profile (mf/deref refs/profile) (let [section (get-in route [:data :name])
section (get-in route [:data :name])
params (parse-params route) params (parse-params route)
project-id (:project-id params) project-id (:project-id params)

View file

@ -514,7 +514,7 @@
[:li {:on-click (partial on-click :settings-password)} [:li {:on-click (partial on-click :settings-password)}
[:span.icon i/lock] [:span.icon i/lock]
[:span.text (tr "labels.password")]] [:span.text (tr "labels.password")]]
[:li {:on-click (partial on-click (du/logout))} [:li {:on-click #(on-click (du/logout) %)}
[:span.icon i/exit] [:span.icon i/exit]
[:span.text (tr "labels.logout")]] [:span.text (tr "labels.logout")]]

View file

@ -17,7 +17,7 @@
(defn load-arengu-sdk (defn load-arengu-sdk
[container-ref email form-id] [container-ref email form-id]
(letfn [(on-init [] (letfn [(on-init []
(let [container (mf/ref-val container-ref)] (when-let [container (mf/ref-val container-ref)]
(-> (.embed js/ArenguForms form-id container) (-> (.embed js/ArenguForms form-id container)
(p/then (fn [form] (.setHiddenField ^js form "email" email)))))) (p/then (fn [form] (.setHiddenField ^js form "email" email))))))

View file

@ -88,6 +88,7 @@
:credentials credentials :credentials credentials
:referrerPolicy "no-referrer" :referrerPolicy "no-referrer"
:signal signal}] :signal signal}]
(-> (js/fetch (str uri) params) (-> (js/fetch (str uri) params)
(p/then (fn [response] (p/then (fn [response]
(vreset! abortable? false) (vreset! abortable? false)

View file

@ -3255,3 +3255,6 @@ msgstr "Owner can't leave team, you must reassign the owner role."
msgid "errors.team-leave.insufficient-members" msgid "errors.team-leave.insufficient-members"
msgstr "Insufficient members to leave team, you probably want to delete it." msgstr "Insufficient members to leave team, you probably want to delete it."
msgid "errors.auth.unable-to-login"
msgstr "Unable to login, looks like you are not authenticated."