Quick one: I’m working on an app using re-frame framework, which uses reagent, itself a ClojureScript wrapper of Facebook’s ReactJS.

One of the earlier tasks is to wire the app up to Auth0, an IaaS, and for simplicity, I’m using their Lock library.

This post works, but is not very re-frame-y.

Effect handler code

(ns your-app.auth0
  (:require [re-frame.core :as re-frame]
            [cljsjs.auth0-lock]))

(defonce lock-instance (atom nil))

(defn *js->clj
  [js]
  (js->clj js :keywordize-keys true))

(defn make-lock
  [client-id domain options on-authenticated]
  (let [lock (js/Auth0Lock. client-id domain
                            (clj->js options))]
    (.on lock "authenticated"
         #(re-frame/dispatch (conj on-authenticated (*js->clj %))))
    lock))

(defn get-user-info
  [lock value]
  (.getUserInfo
   lock (:access-token value)
   (fn [e p]
     (let [error   (*js->clj e)
           profile (*js->clj p)]
       (if e
         (re-frame/dispatch (conj (:on-failure value) error))
         (re-frame/dispatch (conj (:on-success value) profile)))))))

(re-frame/reg-fx
 :lock
 (fn [value]
   (let [lock @lock-instance]
     (case (:method value)
       :instantiate   (reset! lock-instance
                              (make-lock (:client-id value)
                                         (:domain value)
                                         (:options value)
                                         (:on-authenticated value)))
       :show          (.show lock)
       :logout        (.logout lock
                               (clj->js {:returnTo (:return-to value)}))
       :get-user-info (get-user-info lock value)
       nil))))

Event handler code

On app-startup (called with a dispatch-sync)

(re-frame/reg-event-fx
 :initialize-lock
 (fn [_ _]
   {:lock {:method           :instantiate
           :client-id        config/auth0-client-id
           :domain           config/auth0-domain
           :options          lock-options
           :on-authenticated [:new-auth-result]}}))

Log in

(re-frame/reg-event-fx
 :login
 (fn [_ _]
   {:lock {:method :show}}))

On Auth result

(re-frame/reg-event-fx
 :new-auth-result
 (fn [{:keys [db]}
      [_ {new-access-token :accessToken
          new-id-token     :idToken}]]
   {:db          (assoc db
                        :id-token new-id-token
                        :access-token new-access-token)
    :lock        {:method :get-user-info
                  :access-token new-access-token
                  :on-success [:profile-change]
                  :on-failure [:auth-fail]}}))

etc.

Thoughts

We are not using the app-db to store the lock object. But this kind of lives in a separate world anyways.

This followed the pattern set by re-frame-http-fx, callbacks via dispatches and all.

Setting the behaviour of the lock object via the on call on creation feels more appropriate than exposing the on method directly. We are limiting that stateful behaviour to app setup, when we can impose more control on sequence of events.

If we have a relatively simple auth flow, doing things in this re-frame purist way doesn’t add a whole lot more value, as compared to triggering it via side-effects, but it might allow for more complex events.

It might be worth fleshing this out into a full (micro)library. re-auth0-fx?