Using friend in clojars
In the ruby web world there is a great set of libraries for authentication in warden and devise. Earlier this year, Chas Emerick released friend, which is a similar library for clojure. When I first started contributing to clojars, it was using custom authentication and authorization functions. After friend was released I was able to remove most of this code.
Around this time Phil Hagelberg had changed clojars to use
This made it easy to use friend’s
bcrypt-credentials-fn to do the
password checking. It just required querying the database with
find-user-by-user-or-email, and to return the proper map for
:roles are not used in clojars; why is
Interactive Form Workflow
Friend comes with a
interactive-form workflow. It works by
listening for a
/login(by default) and using the
credential function to attempt the login. In the case of success it
will forward to the url with unauthorized access. In the case of
failure it will redirect back to
This was different then the previous clojars authentication style,
which would render the login form directly on an unsuccessful login.
Rather then building a new workflow, the
/login handler was adapted
to check for those params and render a login failed message.
The clojars logout functionality also had to be replaced with friend’s
logout middleware, but that was easy to use.
Friend does not come with a workflow for registering. However, it was easy enough to change the clojars registration pages into one.
/register can just render the
registration as a workflow, it will listen for a
/register and either return a ring response with the form to try
again, or add the user and return a friend “authentication map”. Yes,
this is a different pattern then the interactive workflow above.
Authentication not Authorization
While exploring friend, a downside was noted with the authorization.
When a user is authenticated, the
:roles are added into the session. If
the user gets a new role it will not take effect without a
derive to produce a in memory role hierarchy
can work with this, as suggested in the friend readme.
This seemed like a bad idea for clojars. The direct mapping of groups
to roles would require trying to keep the roles in sync with the
database, and reinitialized on a restart. Instead clojars uses its own
mechanism for authorization wrapping
After discussion with Chas an issue has been filed at https://github.com/cemerick/friend/issues/21. It sounds like there will be work in this area in the future.
Moving to friend allowed removal of several pieces of code. The
ability to deploy to
/repo over https was developed shortly
afterwards. It was nice to wrap a friend middleware with the
http-basic workflow, have it use the same credentials function, and