167 lines
4.7 KiB
Markdown
167 lines
4.7 KiB
Markdown
# 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:
|
|
|
|
```clojure
|
|
(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:
|
|
|
|
```clojure
|
|
(def extractors [jwt-extractor api-key-extractor])
|
|
|
|
(let [app ((wrap-fn extractors) handler)]
|
|
...)
|
|
```
|
|
|
|
Where here are some example of extractors:
|
|
|
|
```clojure
|
|
;; 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:
|
|
|
|
~~~clojure
|
|
(GET "/foo" []
|
|
:identity-info [id-info]
|
|
(... do something with id-info ...))
|
|
~~~
|
|
|
|
and also
|
|
|
|
~~~clojure
|
|
;; 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.
|