From bd833d2f70f6e74eefe6e43b5c1b9f47a15780e4 Mon Sep 17 00:00:00 2001 From: Peter Taoussanis Date: Fri, 5 Feb 2016 12:34:42 +0700 Subject: [PATCH] [#198] Add CSRF token header compatible with ring.middleware defaults, etc. Thanks to @theasp for bringing attention to this --- example-project/src/example/client.cljs | 5 +++-- example-project/src/example/server.clj | 17 +++++--------- src/taoensso/sente.cljx | 30 ++++++++++++------------- 3 files changed, 23 insertions(+), 29 deletions(-) diff --git a/example-project/src/example/client.cljs b/example-project/src/example/client.cljs index 3c6ec00..93dbf86 100644 --- a/example-project/src/example/client.cljs +++ b/example-project/src/example/client.cljs @@ -125,8 +125,9 @@ (sente/ajax-lite "/login" {:method :post - :params {:user-id (str user-id) - :csrf-token (:csrf-token @chsk-state)}} + :headers {:X-CSRF-Token (:csrf-token @chsk-state)} + :params {:user-id (str user-id)}} + (fn [ajax-resp] (->output! "Ajax login response: %s" ajax-resp) (let [login-successful? true ; Your logic here diff --git a/example-project/src/example/server.clj b/example-project/src/example/server.clj index d7918d3..4744532 100644 --- a/example-project/src/example/server.clj +++ b/example-project/src/example/server.clj @@ -115,17 +115,12 @@ (route/not-found "

Page not found

")) (def main-ring-handler - (let [ring-defaults-config - (assoc-in ring.middleware.defaults/site-defaults - [:security :anti-forgery] - {:read-token (fn [req] (-> req :params :csrf-token))})] - - ;; NB: Sente requires the Ring `wrap-params` + `wrap-keyword-params` - ;; middleware to work. These are included with - ;; `ring.middleware.defaults/wrap-defaults` - but you'll need to ensure - ;; that they're included yourself if you're not using `wrap-defaults`. - (ring.middleware.defaults/wrap-defaults - ring-routes ring-defaults-config))) + "**NB**: Sente requires the Ring `wrap-params` + `wrap-keyword-params` + middleware to work. These are included with + `ring.middleware.defaults/wrap-defaults` - but you'll need to ensure + that they're included yourself if you're not using `wrap-defaults`." + (ring.middleware.defaults/wrap-defaults + ring-routes ring.middleware.defaults/site-defaults)) ;;;; Sente event handlers diff --git a/src/taoensso/sente.cljx b/src/taoensso/sente.cljx index abf309b..887f4a8 100644 --- a/src/taoensso/sente.cljx +++ b/src/taoensso/sente.cljx @@ -714,15 +714,7 @@ (if handshake? :handshake :non-handshake) clj) (when handshake? - (let [[_ [?uid ?csrf-token ?handshake-data] :as handshake-ev] clj - ;; Another idea? Not fond of how this places restrictions on the - ;; form and content of ?handshake-data: - ;; handshake-ev [:chsk/handshake - ;; (merge - ;; (have [:or nil? map?] ?handshake-data) - ;; {:?uid ?uid - ;; :?csrf-token ?csrf-token})] - ] + (let [[_ [?uid ?csrf-token ?handshake-data] :as handshake-ev] clj] (when (str/blank? ?csrf-token) (warnf "SECURITY WARNING: no CSRF token available for use by Sente")) @@ -813,8 +805,8 @@ (try (WebSocket. (enc/merge-url-with-query-string url - ;; User params first (don't clobber impl. params): - (merge params {:client-id client-id}))) + (merge params ; 1st (don't clobber impl.): + {:client-id client-id}))) (catch js/Error e (errorf e "WebSocket js/Error") nil))] @@ -888,17 +880,23 @@ (when ?cb-fn (?cb-fn :chsk/closed))) ;; TODO Buffer before sending (but honor `:flush?`) - (do + (let [csrf-token (:csrf-token @state_)] (ajax-lite url (merge ajax-opts {:method :post :timeout-ms ?timeout-ms :resp-type :text ; We'll do our own pstr decoding + :headers + (merge (:headers ajax-opts) ; 1st (don't clobber impl.): + {:X-CSRF-Token csrf-token}) + :params (let [ppstr (pack packer (meta ev) ev (when ?cb-fn :ajax-cb))] - (merge - params ; User params first (don't clobber impl. params): + (merge params ; 1st (don't clobber impl.): {:_ (enc/now-udt) ; Force uncached resp - :csrf-token (:csrf-token @state_) + + ;; A duplicate of X-CSRF-Token for user's convenience and + ;; for back compatibility with earlier CSRF docs: + :csrf-token csrf-token ;; Just for user's convenience here. non-lp-POSTs don't ;; actually need a client-id for Sente's own implementation: @@ -962,7 +960,7 @@ ;; Note that user params here are actually POST params for ;; convenience. Contrast: WebSocket params sent as query ;; params since there's no other choice there. - params ; User params first (don't clobber impl. params) + params ; 1st (don't clobber impl.): {:_ (enc/now-udt) ; Force uncached resp :client-id client-id}