Cannot get Friend to work (login or unauthorized handler)

221 views
Skip to first unread message

Jonathon McKitrick

unread,
Aug 6, 2014, 7:21:22 AM8/6/14
to clo...@googlegroups.com
First, the code:

(ns pts.server
  (:use [compojure.core])
  (:require [ring.adapter.jetty :as jetty]
            [ring.util.response :as response]
            [compojure.handler :as handler]
            [compojure.route :as route]
            [cemerick.friend :as friend]
            (cemerick.friend [workflows :as workflows]
                             [credentials :as creds])))

(defroutes www-routes
  (GET "/locked" [] (friend/authorize #{::admin} "Admin only"))
  (GET "/home" [] (response/file-response "home.html" {:root "resources/public"}))
  (GET "/login" [] (response/file-response "login.html" {:root "resources/public"}))
  (GET "/" [] (response/redirect "index.html"))
  (route/resources "/")
  (route/not-found "Not Found"))

(def app (handler/site www-routes))

(def users {"root" {:username "root"
                    :password (creds/hash-bcrypt "toor")
                    :roles #{::admin}}})

(def secure-app
  (-> app
      (friend/authenticate {:unauthorized-handler #(response/status (response/response "NO") 401)
                            :credential-fn (partial creds/bcrypt-credential-fn users)
                            :workflows [(workflows/interactive-form)]})))

(defn -main [& args]
  (let [port (Integer/parseInt (get (System/getenv) "PORT" "3000"))]
    (jetty/run-jetty secure-app {:port port :join? false})))

It's dead simple, but 2 major things are not working.

1.  The POST to /login to submit the login form gives a 404 Not Found.  Isn't the POST handler part of the friend/authenticate middleware?
2.  Attempts to access the /locked URL throw an exception and a stacktrace, rather than calling the unauthorized handler:
throw+: {:cemerick.friend/required-roles #{:pts.server/admin}, :cemerick.friend/exprs ["Admin only"], :cemerick.friend/type :unauthorized, :cemerick.friend/identity nil}

What am I doing wrong here?

Gary Verhaegen

unread,
Aug 6, 2014, 10:47:16 AM8/6/14
to clo...@googlegroups.com
1. No, you have to provide it (as a non-protected route, obviously).
2. The order in which you apply the handler/site and friend/authenticate middlewares is reversed: friend needs the session (and others), so it should come "after" (or rather "within") the handler/site to work properly (in execution order).
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Jonathon McKitrick

unread,
Aug 6, 2014, 11:49:41 AM8/6/14
to clo...@googlegroups.com
I'm confused.  None of the examples shown implemented the login POST handler.  The docs implied it was already part of the middleware:

From https://github.com/cemerick/friend :
>>>
The example above defines a single workflow — one supporting the POSTing of :username and :password parameters to (by default) /login — which will discover the specified :credential-fn and use it to validate submitted credentials.
<<<


--
Jonathon McKitrick


You received this message because you are subscribed to a topic in the Google Groups "Clojure" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/clojure/yk32Imtd5u8/unsubscribe.
To unsubscribe from this group and all its topics, send an email to clojure+u...@googlegroups.com.

Gary Verhaegen

unread,
Aug 6, 2014, 1:14:51 PM8/6/14
to clo...@googlegroups.com
I was wrong, sorry. Looking at the code for c.f.workflows/interactive-form, you can indeed see where it intercepts a POST request to the provided :login-uri (lines 84-85 on current master).

Which means I have absolutely no idea why it gives you a 404, except maybe if it is related to the other point about the order of middlewares.

Sorry for the confusion.

Gary Verhaegen

unread,
Aug 6, 2014, 1:30:45 PM8/6/14
to clo...@googlegroups.com
I just checked, with the given code, after I switch the order of middlewares, a POST to /login gives me a 302 redirect to /login?&login_failed=Y while a POST with the correct credentials gives me a 303 to /.

I'm sorry I cannot explain why, however.

Nelson Morris

unread,
Aug 6, 2014, 1:32:13 PM8/6/14
to Clojure
The ordering of middleware is certainly causing some problems.  From https://github.com/cemerick/friend#authentication: "Note that Friend itself requires some core Ring middlewares: params, keyword-params and nested-params".  These are added as part of `handler/site`, and so that needs to be outside of `friend/authenticate`, something like `(def app (-> www-routes (friend/authenticate ...) handler/site)`.

Jonathon McKitrick

unread,
Aug 7, 2014, 7:19:33 AM8/7/14
to clo...@googlegroups.com
So here's what I discovered:

If I wrap ONLY the www-routes in Friend and remove api-routes entirely, it works.  So far, I've tried several combinations of route, handler/api, handler/site and friend and I get incorrect results, most often a null page.

Any ideas on how to wrap both handler/api and handler/site routes in Friend?


--
Jonathon McKitrick



For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscribe@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to

For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to a topic in the Google Groups "Clojure" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/clojure/yk32Imtd5u8/unsubscribe.
To unsubscribe from this group and all its topics, send an email to clojure+unsubscribe@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to

For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscribe@googlegroups.com.

Jonathon McKitrick

unread,
Aug 7, 2014, 7:25:49 AM8/7/14
to clo...@googlegroups.com
I think it's sequencing.  I'm going to try swapping the routes for api and site.
Reply all
Reply to author
Forward
0 new messages