Skip to content

Instantly share code, notes, and snippets.

@mping
Forked from Deraen/00_notes.md
Created September 22, 2018 18:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mping/4177f11caf82f4ebdce9d9459c0f073a to your computer and use it in GitHub Desktop.
Save mping/4177f11caf82f4ebdce9d9459c0f073a to your computer and use it in GitHub Desktop.
Compojure-api and Buddy
  • (:identity req) is auth backend independent way to access user data
  • login and logout implementation depends on auth backend
  • :current-user doesn't imply that authentication is required, route should also have :auth-rules if authentication is required
(ns backend.access
(:require [buddy.auth :refer [authenticated?]]))
(defn authenticated [req]
(authenticated? req))
(defn admin [req]
(and (authenticated? req)
(#{:admin} (:role (:identity req)))))
(ns backend.restructure
(:require [compojure.api.meta :refer [restructure-param]]
[backend.access :as access]))
(defn access-error [req val]
(unauthorized val))
(defn wrap-rule [handler rule]
(-> handler
(wrap-access-rules {:rules [{:pattern #".*"
:handler rule}]
:on-error access-error})))
(defmethod restructure-param :auth-rules
[_ rule acc]
(update-in acc [:middlewares] conj [wrap-rule rule]))
(defmethod restructure-param :current-user
[_ binding acc]
(update-in acc [:letks] into [binding `(:identity ~'+compojure-api-request+)]))
(ns backend.handler
(:require [ring.util.http-response :refer [ok unauthorized forbidden]]
[ring.middleware.session :refer [wrap-session]]
[compojure.api.sweet :refer :all]
[buddy.auth :refer [authenticated? throw-unauthorized]]
[buddy.auth.backends.session :refer [session-backend]]
[buddy.auth.accessrules :refer [wrap-access-rules]]
[buddy.auth.middleware :refer [wrap-authentication wrap-authorization]]
[backend.access :as access]
backend.restructure))
; FIXME: From config
(def cookie-name "backend-session")
(def auth-backend
; By default responds with 401 or 403 if unauthorized
(session-backend))
(defn wrap-app-session [handler]
(-> handler
(wrap-authorization auth-backend)
(wrap-authentication auth-backend)
(wrap-session {:cookie-name cookie-name})))
(defapi app'
{:swagger {:ui "/api-docs"
:spec "/swagger.json"}}
(POST "/login" []
(assoc-in (ok) [:session :identity] {:_id 1, :username "juho", :role :admin}))
(POST "/logout" []
(assoc-in (ok) [:session :identity] nil))
(GET "/foo" []
:auth-rules access/authenticated
; :auth-rules {:or [access/authenticated access/other-predicate]}
; :auth-rules {:and [access/authenticated access/other-predicate]}
:current-user user
(ok user))
(def app
(-> app'
wrap-app-session))
(ns backend.handler
(:require [ring.util.http-response :refer [ok unauthorized forbidden]]
[compojure.api.sweet :refer :all]
[buddy.auth :refer [authenticated? throw-unauthorized]]
[buddy.sign.jwt :as jwt]
[buddy.auth.backends :as backends]
[buddy.auth.accessrules :refer [wrap-access-rules]]
[buddy.auth.middleware :refer [wrap-authentication wrap-authorization]]
[backend.access :as access]
backend.restructure))
(def secret "FIXME")
(def auth-backend
(backends/jws {:secret secret
:authfn (fn [token-data]
;; TODO: This option can be used (I think) to
;; validate e.g. token expiration date (return nil if not valid)
;; or to retrieve the real data from database based on value of token
token-data)}))
(defn wrap-app [handler]
(-> handler
;; TODO: If token only contains token-id which refers to database,
;; middleware could be added here to load the data from DB?
(wrap-authorization auth-backend)
(wrap-authentication auth-backend)))
(defapi app
{:swagger {:ui "/api-docs"
:spec "/swagger.json"
;; Adds field to Swagger-UI to provide the value for Authorization header
:data {:securityDefinitions {:api_key {:type "apiKey" :name "Authorization" :in "header"}}}}}
(POST "/login" []
;; Then token is provided, this value is added to request :identity if the token is valid
;; It is good idea to add some data here for token validation, like expiration date
;; or token id that is used to validate token against database.
(ok {:token (jwt/sign {:_id 1 :username "juho"} secret)}))
(GET "/foo" []
:auth-rules access/authenticated
; :auth-rules {:or [access/authenticated access/other-predicate]}
; :auth-rules {:and [access/authenticated access/other-predicate]}
:current-user user
(ok user))
(def app
(-> app'
wrap-app))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment