Skip to content

Instantly share code, notes, and snippets.

@kurtharriger
Last active February 14, 2024 02:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kurtharriger/7bd07ef03a9b28725f6f765f085455ac to your computer and use it in GitHub Desktop.
Save kurtharriger/7bd07ef03a9b28725f6f765f085455ac to your computer and use it in GitHub Desktop.
electric clojure server auth
#?(:cljs (def !client-jwt-token (atom nil)))
(def !user (atom nil))
(e/def user (e/watch !user))
(def ^:dynamic *user* nil)
(e/defn ProvideUserContext []
(e/client
(let [token (.getItem js/localStorage token-local-storage-key)]
(reset! !client-jwt-token token)))
(e/client
(let [token (e/watch !client-jwt-token)]
(.setItem js/localStorage token-local-storage-key token)
; token can techically be decoded on client side
; but need the public key to validate signature and have not
; yet created a endpoint to expose public key
; it can also be decoded without validation but an inavlid
; token will just result in future errors when token is validated
; on server
(reset! !user (when token (e/server (jwt/read-token token)))))))
#?(:clj
(defn print-user []
(println "user" *user*)
(println "user" user)))
#?(:clj
(defn with-auth [token f & args]
(binding [*user* (when token (jwt/read-token token))]
(apply f args))))
(e/defn PrintUserButton []
(e/client
(ui/button
(e/fn []
(let [token (e/watch !client-jwt-token)]
(println "token" token)
(e/server
(println "token" token)
; electric seems to intercept binding and complain *user* its not an electric def
; further more if I make it a electric def it semes to be wrapped in
; #object[hyperfiddle.electric.impl.lang$electric_only
#_(binding [user (when token (jwt/read-token token))]
(print-user))
(with-auth token print-user))))
(dom/text "Print User"))))
@kurtharriger
Copy link
Author

kurtharriger commented Feb 14, 2024

Better solution: add atom to the e/http-request object and store data there

#?(:cljs (def token-local-storage-key "shomp-jwt"))
#?(:cljs (def !client-jwt-token (atom nil)))
#?(:cljs (def !user (atom nil)))
;; e/server blocks should use @(::user e/http-request) 
;; passing unsigned user data from client to server
;; is not secure and a malicious client could claim 
;; alter data before sending to server and claim an
;; alternate identity
#?(:cljs (e/def user (e/watch !user)))
#?(:cljs (defn set-client-token! [token]
           (reset! !client-jwt-token token)))

; on mount read client token from local storage
(e/defn ProvideUserContext []
  (e/client
    (let [token (.getItem js/localStorage token-local-storage-key)]
      (reset! !client-jwt-token token)))
  (e/client
    (let [token (e/watch !client-jwt-token)]
      (.setItem js/localStorage token-local-storage-key token)
      (reset! !user
        (e/server
          (let [claims (when token (jwt/read-token token))]
            (reset! (::user e/http-request) claims)
            claims))))))


(e/defn TestUserToken []
  (e/client
    (dom/div
      (dom/text "token" (e/watch !client-jwt-token)))
    (dom/div
      (dom/text "user" (e/watch !user)))
    (dom/div
      (ui/button
        (e/fn []
          (e/client
            (print (e/server 
                     (let [user @(::user e/http-request)]
                       (print "user" user )
                       user)))))
        (dom/text "Print server value")))
    (dom/div
      (ui/button
        (e/fn []
          (e/client
            (let [token (e/server
                          (let [randid (str (rand-int 7777))]
                            (jwt/create-token {:sub randid :username (str "user-" randid) :phone randid})))]
              (println "setting token" token)
              (set-client-token! token))))
        (dom/text "Random User token")))))

(e/defn Main [cfg]
 (e/server 
  (binding [e/http-request (-> (:ring-request cfg) (assoc ::user (atom nil)))
            conn (:conn cfg)]
            (prn e/http-request)
   (e/client
     (ProvideUserContext. )
     (binding [dom/node js/document.body]
       (TestUserToken.))))))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment