‹ gerlach.coffee

IP Whitelist OR HTTP Basic Auth with Ring

Oct 28, 2018

For work I recently needed to implement scenario: The web app should be freely acessible within the office (which has a static IP address to the world) OR from home using HTTP basic auth. This wasn’t super straightfoward, so here is my implementation:

I used two ring libraries:

with these deps.edn coordinates:

  net.danielcompton/ring-ip-whitelist {:mvn/version "0.2.1"}
  ring-basic-authentication           {:mvn/version "1.0.5"}

To implement my specific scheme, I pulled out the pieces I needed and wrapped them up in a new ring middleware:

(require '[ring.middleware.basic-authentication :refer [authentication-failure basic-authentication-request]]
          '[ring.middleware.ip-whitelist :refer [ip-in-ip-set? build-ip-set]])

(def office-ip "")

(def allowed-users-password-pairs #{["max" "coffee"]
                                    ["jane" "latte"]})

(defn basic-auth-authenticated?
  "Checks if the username/password combination is valid"
  [name pass]
  (allowed-users-password-pairs [name pass]))

(defn ip-whitelisted? [whitelist req]
  "Checks if the IP is in the whitelist. Also works with CIDR blocks."
  (ip-in-ip-set? (build-ip-set whitelist) (:remote-addr req)))

(defn wrap-auth [handler]
  (fn [req]
    (if-not (ip-whitelisted? [office-ip] req)
        (let [auth-req (basic-authentication-request req basic-auth-authenticated?)]
          (if (:basic-authentication auth-req)
            (handler auth-req) ; authenticated!
            (authentication-failure)))) ; no authentication. Present challenge
      (do ; ip is whitelisted
        (handler req)))))