Search code examples
clojurebasic-authenticationcompojurering

Compojure: access request params from inside basic-auth function?


I'm using the ring basic-authentication library available for compojure. The authenticated? function takes a username and a password in order to authenticate, but in my particular case I need to access other parameters passed in by the user request besides a username and a password.

For instance, consider a case where there are multiple servers, and a user has an account on a particular server. That user would therefore need to authenticate with (log-on to) a particular server. Therefore I need his username, password, AND server to do the authentication.

Handling a "normal" case which just calls for a username and password might look something like this (using example sql to hit a database):

; curl my_id:my_pass@localhost/my_request

(defn authenticated? [id password]
  (first (select user (where {:id id :password password}) (limit 1))))

I'd like to do something like this:

; curl my_id:my_pass@localhost/my_server/my_request

(defn authenticated? [id password ??server??]
  (first (select user (where {:server server :id id :password password}) (limit 1))))

I guess my question is, how do I access all request params from inside authenticated? Or, alternatively, how do I pass the server id into the authenticated? function?

Thanks.


Solution

  • In accordance with the comments to the question above, the approach would look like this (just a sketch, I haven't tested whether this works due to lack of a running setup with ring):

    (defn wrap-basic-auth-server [handler authfn]
     (fn [request]
       (-> request
           (wrap-basic-authentication (partial authfn (get-in request [:params :server])))
           handler)))
    

    What's happening here is that the code is assuming that your server url param will have been added by wrap-params (from ring.middleware.params) to the request map. wrap-basic-authentication is called then with the handler (typical ring middleware, i.e. any other wrapper / handler coming afterwards) and a new (partial) function, which is just your authenticate function which has already swallowed the server arg.

    And then instead of just calling wrap-basic-authentication in your routes, you need to add wrap-params and wrap-basic-auth-server and you need your new auth function. E.g.

    (defn authenticated? [server user pass]
       ... your code ...)
    
    (def app 
        (-> app-routes
            ... potential other wrappers elided ...
            (wrap-params)
            (wrap-basic-auth-server authenticated?)))
    

    As I said, I've not tested this code, but it should get you started.