(: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
Compojure-api and Buddy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns backend.access | |
(:require [buddy.auth :refer [authenticated?]])) | |
(defn authenticated [req] | |
(authenticated? req)) | |
(defn admin [req] | |
(and (authenticated? req) | |
(#{:admin} (:role (:identity req))))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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+)])) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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
@Deraen thank you so much for publishing this gist! Really helped me pull the pieces together on the Buddy addition to my Compojure API project. ✨