I am trying to integrate Friend authentication and authorisation into a Clojure/Compojure single-page web application.
I have a login form backed by an Angular controlle
You need to have :redirect-on-auth?
as false
and you need to wrap your response in a response map {:status 200 :body (workflows/make-auth...)}
. Note that you probably need to serialize your body to a String
or something else that can be handled.
I've built a working solution to your issue. The key is to make sure you return a status, body, and merge the the authentication credentials into response. So you want to return
{:status 200 :body {:message "Hello World"}
:session {:cemerick.friend/identity {...}} ...}
The problem with the solution above is that it merges the session results into the body, instead of into ring response itself. Gist
(defn- form-ajax-response
"Creates an Ajax Request.
Authenticates User by merging authentication
into response along with status and body allowing
for ajax response."
[status body resp auth content-type]
(let [auth-resp (friend/merge-authentication (dissoc resp :body-params) auth)
formatter (case content-type
"application/edn" noir.response/edn
noir.response/json)]
(merge auth-resp {:status status} (formatter {:body body}))))
(def not-nil? (complement nil?))
(defn form-or-ajax-login
"Friend Workflow for Handling forms or ajax requests posted to
'/login'. When a form is posted, the request will initiate a redirect
on login When the post is performed via ajax. A response will be sent
with success and redirect information"
[& {:keys [login-uri credential-fn login-failure-handler redirect-on-auth?] :as ajax-config
:or {redirect-on-auth? true}}]
(fn [{:keys [uri request-method content-type params] :as request}]
(when (and (= :post request-method)
(= uri (gets :login-uri ajax-config (::friend/auth-config request))))
(let [creds {:username (:username params)
:password (:password params)}
{:keys [username password]} creds
ajax? (not-nil? (ajax-request-type content-type))
cred-fn (gets :credential-fn ajax-config (::friend/auth-config request))]
(when-let [user-record (and username password (cred-fn creds))]
(let [user-auth (workflow/make-auth user-record {::friend/workflow :form-or-ajax
::friend/redirect-on-auth? false})]
(case ajax?
true (form-ajax-response 202 {:Tom "Peterman"} request user-auth content-type)
user-auth)))))))