|
diff --git a/src/com/biffweb/impl/auth.clj b/src/com/biffweb/impl/auth.clj |
|
index fe991cc..26397b6 100644 |
|
--- a/src/com/biffweb/impl/auth.clj |
|
+++ b/src/com/biffweb/impl/auth.clj |
|
@@ -3,6 +3,7 @@ |
|
[com.biffweb.impl.rum :as brum] |
|
[com.biffweb.impl.time :as btime] |
|
[com.biffweb.impl.util :as butil] |
|
+ [com.biffweb.impl.util.ns :as biff.util.ns] |
|
[com.biffweb.impl.xtdb :as bxt] |
|
;;; NOTE: if you copy this file into your own project, remove the |
|
;;; above lines and replace them with the com.biffweb namespace: |
|
@@ -29,16 +30,20 @@ |
|
(not (re-find #"\s" email)))) |
|
|
|
(defn new-link [{:keys [biff.auth/check-state |
|
+ biff.auth/extra-params |
|
biff/base-url |
|
biff/secret |
|
- anti-forgery-token]} |
|
+ anti-forgery-token |
|
+ params]} |
|
email] |
|
(str base-url "/auth/verify-link/" |
|
(bmisc/jwt-encrypt |
|
- (cond-> {:intent "signin" |
|
- :email email |
|
- :exp-in (* 60 60)} |
|
- check-state (assoc :state (butil/sha256 anti-forgery-token))) |
|
+ (merge (select-keys params extra-params) |
|
+ {:intent "signin" |
|
+ :email email |
|
+ :exp-in (* 60 60)} |
|
+ (when check-state |
|
+ {:state (butil/sha256 anti-forgery-token)})) |
|
(secret :biff/jwt-secret)))) |
|
|
|
(defn new-code [length] |
|
@@ -82,9 +87,10 @@ |
|
path-params |
|
params |
|
anti-forgery-token]}] |
|
- (let [{:keys [intent email state]} (-> (merge params path-params) |
|
- :token |
|
- (bmisc/jwt-decrypt (secret :biff/jwt-secret))) |
|
+ (let [{:keys [intent email state] |
|
+ :as token-params} (-> (merge params path-params) |
|
+ :token |
|
+ (bmisc/jwt-decrypt (secret :biff/jwt-secret))) |
|
valid-state (= state (butil/sha256 anti-forgery-token)) |
|
valid-email (= email (:email params))] |
|
(cond |
|
@@ -92,7 +98,7 @@ |
|
{:success false :error "invalid-link"} |
|
|
|
(or (not check-state) valid-state valid-email) |
|
- {:success true :email email} |
|
+ (assoc token-params :success true) |
|
|
|
(some? (:email params)) |
|
{:success false :error "invalid-email"} |
|
@@ -126,6 +132,21 @@ |
|
:else |
|
{:success true :email email :code code :user-id @user-id}))) |
|
|
|
+(defn query-encode [s] |
|
+ (some-> s |
|
+ (java.net.URLEncoder/encode "UTF-8") |
|
+ (str/replace "+" "%20"))) |
|
+ |
|
+(defn append-extra-params [{:keys [biff.auth/extra-params params]} url] |
|
+ ;; This assumes that url already has at least one query param in it, since the |
|
+ ;; appended part starts with a "&" |
|
+ (apply str |
|
+ url |
|
+ (for [k extra-params |
|
+ :let [v (get params k)] |
|
+ :when v] |
|
+ (str "&" (name k) "=" (query-encode v))))) |
|
+ |
|
;;; HANDLERS ------------------------------------------------------------------- |
|
|
|
(defn send-link-handler [{:keys [biff.auth/single-opt-in |
|
@@ -135,7 +156,7 @@ |
|
:as ctx}] |
|
(let [{:keys [success error email user-id]} (send-link! ctx)] |
|
(when (and success single-opt-in (not user-id)) |
|
- (bxt/submit-tx (assoc ctx :biff.xtdb/retry false) (new-user-tx ctx email))) |
|
+ (bxt/submit-tx (assoc ctx :biff.xtdb/retry false) (new-user-tx ctx (assoc params :email email)))) |
|
{:status 303 |
|
:headers {"location" (if success |
|
(str "/link-sent?email=" (:email params)) |
|
@@ -150,11 +171,11 @@ |
|
params |
|
path-params] |
|
:as ctx}] |
|
- (let [{:keys [success error email]} (verify-link ctx) |
|
+ (let [{:keys [success error email] :as token-params} (verify-link ctx) |
|
existing-user-id (when success (get-user-id (xt/db node) email)) |
|
token (:token (merge params path-params))] |
|
(when (and success (not existing-user-id)) |
|
- (bxt/submit-tx ctx (new-user-tx ctx email))) |
|
+ (bxt/submit-tx ctx (new-user-tx ctx token-params))) |
|
{:status 303 |
|
:headers {"location" (cond |
|
success |
|
@@ -174,6 +195,7 @@ |
|
|
|
(defn send-code-handler [{:keys [biff.auth/single-opt-in |
|
biff.auth/new-user-tx |
|
+ biff.auth/extra-params |
|
biff/db |
|
params] |
|
:as ctx}] |
|
@@ -186,10 +208,10 @@ |
|
:biff.auth.code/created-at :db/now |
|
:biff.auth.code/failed-attempts 0}] |
|
(when (and single-opt-in (not user-id)) |
|
- (new-user-tx ctx email))))) |
|
+ (new-user-tx ctx (assoc params :email email)))))) |
|
{:status 303 |
|
:headers {"location" (if success |
|
- (str "/verify-code?email=" (:email params)) |
|
+ (append-extra-params ctx (str "/verify-code?email=" (:email params))) |
|
(str (:on-error params "/") "?error=" error))}})) |
|
|
|
(defn verify-code-handler [{:keys [biff.auth/app-path |
|
@@ -212,7 +234,7 @@ |
|
success |
|
(concat [[::xt/delete (:xt/id code)]] |
|
(when-not existing-user-id |
|
- (new-user-tx ctx email))) |
|
+ (new-user-tx ctx (assoc params :email email)))) |
|
|
|
(and (not success) |
|
(some? code) |
|
@@ -228,7 +250,7 @@ |
|
:session (assoc session :uid (or existing-user-id |
|
(get-user-id (xt/db node) email)))} |
|
{:status 303 |
|
- :headers {"location" (str "/verify-code?error=invalid-code&email=" email)}}))) |
|
+ :headers {"location" (append-extra-params ctx (str "/verify-code?error=invalid-code&email=" email))}}))) |
|
|
|
(defn signout [{:keys [session]}] |
|
{:status 303 |
|
@@ -237,10 +259,15 @@ |
|
|
|
;;; ---------------------------------------------------------------------------- |
|
|
|
-(defn new-user-tx [ctx email] |
|
- [{:db/doc-type :user |
|
- :db.op/upsert {:user/email email} |
|
- :user/joined-at :db/now}]) |
|
+(defn new-user-tx [{:biff.auth/keys [extra-params]} {:keys [email] :as params}] |
|
+ [(-> params |
|
+ (select-keys extra-params) |
|
+ ;; Adds a `:user/` namespace to the keys in params, e.g. changes |
|
+ ;; :display-name to :user/display-name |
|
+ (biff.util.ns/select-ns-as nil 'user) |
|
+ (merge {:db/doc-type :user |
|
+ :db.op/upsert {:user/email email} |
|
+ :user/joined-at :db/now}))]) |
|
|
|
(defn get-user-id [db email] |
|
(bxt/lookup-id db :user/email email)) |
|
@@ -249,6 +276,7 @@ |
|
#:biff.auth{:app-path "/app" |
|
:invalid-link-path "/signin?error=invalid-link" |
|
:check-state true |
|
+ :extra-params [] |
|
:new-user-tx new-user-tx |
|
:get-user-id get-user-id |
|
:single-opt-in false |
|
diff --git a/starter/src/com/example.clj b/starter/src/com/example.clj |
|
index 63fafb1..7225fff 100644 |
|
--- a/starter/src/com/example.clj |
|
+++ b/starter/src/com/example.clj |
|
@@ -17,7 +17,7 @@ |
|
|
|
(def modules |
|
[app/module |
|
- (biff/authentication-module {}) |
|
+ (biff/authentication-module {:biff.auth/extra-params [:display-name]}) |
|
home/module |
|
schema/module |
|
worker/module]) |
|
diff --git a/starter/src/com/example/app.clj b/starter/src/com/example/app.clj |
|
index 265dab1..668f5f3 100644 |
|
--- a/starter/src/com/example/app.clj |
|
+++ b/starter/src/com/example/app.clj |
|
@@ -92,10 +92,10 @@ |
|
(map message (sort-by :msg/sent-at #(compare %2 %1) messages))]])) |
|
|
|
(defn app [{:keys [session biff/db] :as ctx}] |
|
- (let [{:user/keys [email foo bar]} (xt/entity db (:uid session))] |
|
+ (let [{:user/keys [email foo bar display-name]} (xt/entity db (:uid session))] |
|
(ui/page |
|
{} |
|
- [:div "Signed in as " email ". " |
|
+ [:div "Signed in as " display-name " (" email "). " |
|
(biff/form |
|
{:action "/auth/signout" |
|
:class "inline"} |
|
diff --git a/starter/src/com/example/home.clj b/starter/src/com/example/home.clj |
|
index c19235d..19ab383 100644 |
|
--- a/starter/src/com/example/home.clj |
|
+++ b/starter/src/com/example/home.clj |
|
@@ -22,18 +22,21 @@ |
|
(biff/recaptcha-callback "submitSignup" "signup") |
|
[:h2.text-2xl.font-bold (str "Sign up for " settings/app-name)] |
|
[:.h-3] |
|
- [:.flex |
|
+ [:.flex.flex-col.gap-2 |
|
[:input#email {:name "email" |
|
:type "email" |
|
:autocomplete "email" |
|
:placeholder "Enter your email address"}] |
|
- [:.w-3] |
|
- [:button.btn.g-recaptcha |
|
- (merge (when site-key |
|
- {:data-sitekey site-key |
|
- :data-callback "submitSignup"}) |
|
- {:type "submit"}) |
|
- "Sign up"]] |
|
+ [:input {:name "display-name" |
|
+ :type "text" |
|
+ :placeholder "Enter your name"}] |
|
+ [:div |
|
+ [:button.btn.g-recaptcha |
|
+ (merge (when site-key |
|
+ {:data-sitekey site-key |
|
+ :data-callback "submitSignup"}) |
|
+ {:type "submit"}) |
|
+ "Sign up"]]] |
|
(when-some [error (:error params)] |
|
[:<> |
|
[:.h-1] |
|
@@ -92,18 +95,22 @@ |
|
(biff/recaptcha-callback "submitSignin" "signin") |
|
[:h2.text-2xl.font-bold "Sign in to " settings/app-name] |
|
[:.h-3] |
|
- [:.flex |
|
+ [:.flex.flex-col.gap-2 |
|
[:input#email {:name "email" |
|
:type "email" |
|
:autocomplete "email" |
|
:placeholder "Enter your email address"}] |
|
- [:.w-3] |
|
- [:button.btn.g-recaptcha |
|
- (merge (when site-key |
|
- {:data-sitekey site-key |
|
- :data-callback "submitSignin"}) |
|
- {:type "submit"}) |
|
- "Sign in"]] |
|
+ ;; Uncomment this if you want to use the 6-digit-code flow for signup |
|
+ #_[:input {:name "display-name" |
|
+ :type "text" |
|
+ :placeholder "Enter your name"}] |
|
+ [:div |
|
+ [:button.btn.g-recaptcha |
|
+ (merge (when site-key |
|
+ {:data-sitekey site-key |
|
+ :data-callback "submitSignin"}) |
|
+ {:type "submit"}) |
|
+ "Sign in"]]] |
|
(when-some [error (:error params)] |
|
[:<> |
|
[:.h-1] |
|
@@ -129,7 +136,7 @@ |
|
(biff/form |
|
{:action "/auth/verify-code" |
|
:id "code-form" |
|
- :hidden {:email (:email params)}} |
|
+ :hidden (select-keys params [:email :display-name])} |
|
(biff/recaptcha-callback "submitCode" "code-form") |
|
[:div [:label {:for "code"} "Enter the 6-digit code that we sent to " |
|
[:span.font-bold (:email params)]]] |
|
diff --git a/starter/src/com/example/schema.clj b/starter/src/com/example/schema.clj |
|
index e8e979a..33d3a38 100644 |
|
--- a/starter/src/com/example/schema.clj |
|
+++ b/starter/src/com/example/schema.clj |
|
@@ -5,6 +5,7 @@ |
|
:user [:map {:closed true} |
|
[:xt/id :user/id] |
|
[:user/email :string] |
|
+ [:user/display-name :string] |
|
[:user/joined-at inst?] |
|
[:user/foo {:optional true} :string] |
|
[:user/bar {:optional true} :string]] |