No description
Find a file
2018-02-01 17:29:01 +01:00
doc initial commit 2017-11-07 17:12:23 +01:00
src/ring_homogeneous_auth_middleware closer to OAuth2 vocabulary 2018-02-01 17:21:57 +01:00
test/ring_homogeneous_auth_middleware initial commit 2017-11-07 17:12:23 +01:00
.gitignore initial commit 2017-11-07 17:12:23 +01:00
CHANGELOG.md initial commit 2017-11-07 17:12:23 +01:00
LICENSE initial commit 2017-11-07 17:12:23 +01:00
project.clj bump version 2018-02-01 17:28:22 +01:00
README.md closer to OAuth2 vocabulary 2018-02-01 17:21:57 +01:00

ring-homogeneous-auth-middleware

A Clojure library designed to homogenise many different auth middleware.

Usage

Generally each auth middleware add the auth informations to the ring-request hash-map.

So for example a ring-jwt-auth middleware will add a :jwt field containing some informations about the identity and auth details.

Another middleware could also be used, for example one might want to accept JWT and long term API keys. The other middleware could then add a :api-key-infos field to the hash-map whose value could be some other kind of information.

This middleware is a simple way to merge all those different informations in a centralized and normalized way. The middelware takes multiple extractors as parameters. An extractor is a function that given a ring-request extract an IdentityInfo or nil.

An IdentityInfo is defined as:

(s/defschema User
  "An User should be understood as a unique entity able to be identified.
  An user must have an unique id and also a name.

  An user could also contain many meta fields that could be provided as meta
  data by some authentication layer. Typically, an email, a phone number, etc...
  "
  (st/merge
   {:id s/Str}
   ;; could contain other meta datas (name, nickname, email, address, phone number, etc...)
   {s/Keyword s/Any}))

(s/defschema Org
  "An Org can be understood as a Community of People, an Organization, a
  Business, etc... This also can be thought as a UNIX group.

  Mainly this should provide a way to filter document for an organization.

  A group must have an unique identifier and a name.

  A group could also have some meta informations. For example, a physical
  address, an Identity Provider URL, etc.."
  (st/merge
   {:id s/Str}
   ;; could contain other meta datas (name, Identity Provider URL, etc...)
   {s/Keyword s/Any}))

(def Scope
  "The scope of an user.

  The scope is a string without any space.
  Mainly this should provide a way to filter route access.

  Typical values are:

  - \"admin\"
  - \"service\"
  - \"service/subservice:read-only\"

  etc..."
  s/Str)

(s/defschema IdentityInfo
  "An IdentityInfo provide the information to identify and determine the
  permissions relative to some request.

  It provide an user, a main org and a set of scopes.

  It is important to note that scopes aren't associated to an user but to an
  IdentityInfo. This enable the same user to provide different scopes via
  different API-Key for example.

  An IdentityInfo while having some mandatory informations could also contains
  some other informations generally for dealing with technical details and ease
  the debugging.

  But they could also be used to extend the actual spec.
  For example, we could imagine that we might want to associate a set of orgs
  to an identity.

  But that's out of the scope of this specific spec."
  (st/merge
   {:user   User
    :org    Org
    :scopes #{Scope}}
   {s/Keyword s/Any}))

Then the middleware will passe the ring request through all extractors and the first return successful extractor will add an :identity-info field to the ring request.

It is used that way:

(def extractors [jwt-extractor api-key-extractor])

(let [app ((wrap-fn extractors) handler)]
  ...)

Where here are some example of extractors:

;; Extractor code example for some JWT

(s/defn extract-identity-infos :- IdentityInfo
  [jwt-info]
  {:user {:id (:sub jwt-info)
          :name (:sub jwt-info)
          :email (:user_email jwt-info)}
   :groups #{{:id (:org_guid jwt-info)
              :name (:org_name jwt-info)}}
   :scopes (if (= "true"
                 ;; this test handle the case when :user_admin is a string
                 ;; and when its a boolean
                 (str (:user_admin jwt-info)))
            #{"admin" "user"}
            #{"user"})
   :auth-type :jwt})

(s/defn jwt-extractor :- (s/maybe IdentityInfo)
  [req]
  (some-> req
          :jwt
          extract-identity-infos))

;; Extractor code example for API Key considering thay :api-key-info field
;; already contains an IdentityInfo

(s/defn api-key-extractor :- (s/maybe IdentityInfo)
  [req]
  (some-> req
          :api-key-infos
          (assoc :auth-type :api-key)))

Furthermore this middleware also provides the ability to destructure information if you use compojure-api. Typically you could:

(GET "/foo" []
     :identity-info [id-info]
     (... do something with id-info ...))

and also

 ;; only user with the role :admin could access this route
(GET "/foo" []
     :roles-filter #{:admin}
     ...)

License

Copyright © 2017 Cisco

Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.