3
votes

I have two sets of compojure routes, public ones, which need no authentication, and private ones which need authentication.

(defroutes public-routes
  (GET "/" [] homepage-handler))

(defroutes private-routes
  (GET "/secrets" [] secrets-handler))

I created a middleware which checks is the user authenticated and either continues the middleware chain or raises.

(defn wrap-must-be-authenticated [handler]
  (fn [request]
    (if (authenticated? request)
      (handler request)
      (throw-unauthorized))))

(def app
  (-> private-routes
      (wrap-must-be-authenticated)))

This works fine, all "private routes" require authentication.

How would I go about adding the public-routes so they are excluded from wrap-must-be-authenticated?

I believe defroutes returns ring handlers, so I'm thinking I need to do something like:

(-> (wrap-must-be-authenticated private-routes)
     public-routes)
1

1 Answers

4
votes

One way to do this is to put multiple routes definitions in a containing routes, and wrap (wrap-routes) the appropriate routes in middleware to restrict access:

(def all-routes
  (routes
    (-> #'private-routes
        (wrap-routes wrap-must-be-authenticated))

    #'public-routes

    (route/not-found
      (:body
        (error-page {:status 404
                     :title "page not found"})))))

Another example from a project where I'm using buddy.auth's restrict:

(defn wrap-admin [handler]
  (restrict handler {:handler (fn [req]
                                (boolean (get-in req [:session :admin?])))}))

(def app-routes
  (routes
    (-> #'admin-routes
        (wrap-routes wrap-admin)
        (wrap-routes middleware/wrap-csrf)
        (wrap-routes middleware/wrap-formats))
    (-> #'home-routes
        (wrap-routes middleware/wrap-csrf)
        (wrap-routes middleware/wrap-formats))))