Skip to content

Commit 5e4c2ff

Browse files
committed
Pass CSRF token using Sec-WebSocket-Protocol header
The proposed change tries to respect other values in the Sec-WebSocket-Protocol header, that the caller may have set in the headers option when calling make-channel-socket-client! Use the pre-ws-handshake option introduced in http-kit commit to Add the Sec-WebSocket-Protocol response header in the websocket upgrade. Some browsers (like Google Chrome and Google Chrome derived ones) fail to complete the upgrade if the upgrade response doesn't include the Sec-WebSocket-Protocol with one of the sub-protocols proposed in the request. [Re: 465]
1 parent ae7a2e7 commit 5e4c2ff

File tree

2 files changed

+43
-11
lines changed

2 files changed

+43
-11
lines changed

src/taoensso/sente.cljc

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,8 @@
282282
(.toByteArray out))
283283
x)))
284284

285+
(def ^:private sente-csrf-token-prefix "sente-csrf-token-")
286+
285287
(defn make-channel-socket-server!
286288
"Takes a web server adapter[1] and returns a map with keys:
287289
@@ -527,6 +529,20 @@
527529
;; undefined):
528530
nil)
529531

532+
sente-csrf-token-pred
533+
(fn [s]
534+
(when (str/starts-with? s sente-csrf-token-prefix)
535+
(subs s (count sente-csrf-token-prefix))))
536+
537+
ws-csrf-token
538+
(fn [ring-req]
539+
(let [ws? (= "websocket" (get-in ring-req [:headers "upgrade"]))
540+
sec-websocket-protocol (get-in ring-req [:headers "sec-websocket-protocol"])
541+
protocol-vals (when (and ws? (string? sec-websocket-protocol))
542+
(-> sec-websocket-protocol
543+
(str/split #", *")))]
544+
(some sente-csrf-token-pred protocol-vals)))
545+
530546
bad-csrf?
531547
(fn [ring-req]
532548
(if (nil? csrf-token-fn)
@@ -538,7 +554,8 @@
538554
(or
539555
(get-in ring-req [:params :csrf-token])
540556
(get-in ring-req [:headers "x-csrf-token"])
541-
(get-in ring-req [:headers "x-xsrf-token"]))]
557+
(get-in ring-req [:headers "x-xsrf-token"])
558+
(ws-csrf-token ring-req))]
542559

543560
(not
544561
(enc/const-str=
@@ -688,6 +705,13 @@
688705
:?reply-fn ?reply-fn
689706
:uid uid}))))
690707

708+
pre-ws-handshake
709+
(fn [server-ch ring-req]
710+
(let [csrf-token (ws-csrf-token ring-req)]
711+
{:do-handshake true
712+
:handshake-extra-headers {"Sec-WebSocket-Protocol"
713+
(str sente-csrf-token-prefix csrf-token)}}))
714+
691715
send-handshake!
692716
(fn [server-ch websocket?]
693717
(trove/log!
@@ -921,7 +945,8 @@
921945
:on-open on-open
922946
:on-msg on-msg
923947
:on-close on-close
924-
:on-error on-error}))))))}))
948+
:on-error on-error
949+
:pre-ws-handshake pre-ws-handshake}))))))}))
925950

926951
(def ^:dynamic *simulated-bad-conn-rate*
927952
"Debugging tool. Proportion ∈ℝ[0,1] of connection activities to sabotage."
@@ -1203,7 +1228,8 @@
12031228
(enc/oget goog/global "MozWebSocket")
12041229
(enc/oget @?node-npm-websocket_ "w3cwebsocket"))]
12051230
(delay
1206-
(let [socket (WebSocket. uri-str)]
1231+
(let [protocols (or (:sec-websocket-protocol headers) [])
1232+
socket (WebSocket. uri-str protocols)]
12071233
(doto socket
12081234
(aset "binaryType" binary-type)
12091235
(aset "onerror" on-error)
@@ -1435,13 +1461,18 @@
14351461
{:on-error on-error
14361462
:on-message on-message
14371463
:on-close on-close
1438-
:headers headers
1464+
:headers (update headers :sec-websocket-protocol
1465+
(fn [x]
1466+
(let [csrf-token (str sente-csrf-token-prefix
1467+
(get-client-csrf-token-str :dynamic (:csrf-token @state_)))]
1468+
(cond
1469+
(string? x) [x csrf-token]
1470+
(coll? x) (conj x csrf-token)
1471+
:else csrf-token))))
14391472
:uri-str
14401473
(enc/merge-url-with-query-string url
14411474
(merge params ; 1st (don't clobber impl.):
1442-
{:client-id client-id
1443-
:csrf-token (get-client-csrf-token-str :dynamic
1444-
(:csrf-token @state_))}))}))
1475+
{:client-id client-id}))}))
14451476

14461477
(catch :any t
14471478
(trove/log! {:level :error, :id :sente.client/ws-constructor-error, :error t}))))]

src/taoensso/sente/server_adapters/http_kit.clj

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,15 @@
1818
(deftype HttpKitServerChanAdapter []
1919
i/IServerChanAdapter
2020
(ring-req->server-ch-resp [sch-adapter ring-req callbacks-map]
21-
(let [{:keys [on-open on-close on-msg _on-error]} callbacks-map
21+
(let [{:keys [on-open on-close on-msg _on-error pre-ws-handshake]} callbacks-map
2222
ws? (:websocket? ring-req)]
2323

2424
;; Note `as-channel` requires http-kit >= v2.4.0
2525
;; Returns {:body <http-kit-implementation-channel> ...}:
2626
(hk/as-channel ring-req
27-
{:on-close (when on-close (fn [sch status-kw] (on-close sch ws? status-kw)))
28-
:on-receive (when on-msg (fn [sch msg] (on-msg sch ws? msg)))
29-
:on-open (when on-open (fn [sch ] (on-open sch ws?)))}))))
27+
{:on-close (when on-close (fn [sch status-kw] (on-close sch ws? status-kw)))
28+
:on-receive (when on-msg (fn [sch msg] (on-msg sch ws? msg)))
29+
:on-open (when on-open (fn [sch ] (on-open sch ws?)))
30+
:pre-ws-handshake (when pre-ws-handshake pre-ws-handshake)}))))
3031

3132
(defn get-sch-adapter [] (HttpKitServerChanAdapter.))

0 commit comments

Comments
 (0)