Friend
An extensible authentication and authorization library for Clojure Ring web applications and services.
Install / Use
/learn @cemerick/FriendREADME
This library is currently unmaintained. You might want to investigate forks for additional enhancements, etc., or alternative approaches generally (e.g. I hear people like Buddy)
Friend 
An extensible authentication and authorization library for Clojure/Ring web applications and services.
Picking up his staff he stood before the rock and said in a clear voice:
Mellon!
The star shone out briefly and faded again. Then silently a great
doorway was outlined, though not a crack or joint had been visible
before. Slowly it divided in the middle and swung outwards inch by inch,
until both doors lay back against the wall. Through the opening a
shadowy stair could be seen climbing steeply up; but beyond the lower
steps the darkness was deeper than the night. The Company stared in
wonder.
"I was wrong after all," said Gandalf, "and Gimli too. Merry, of all
people, was on the right track. The opening word was inscribed on the
archway all the time! The translation should have been: Say 'Friend' and
enter. I had only to speak the Elvish word for friend and the doors
opened. Quite simple. Too simple for a learned lore master in these
suspicious days. Those were happier times. Now let us go!"
— J.R.R. Tolkien, Lord of the Rings
Overview
Friend is intended to provide a foundation for addressing all of the authentication and authorization concerns associated with web apps:
- user agent authentication; Friend currently includes support for form and
HTTP Basic authentication, and makes it easy to:
- implement and use other workflows (e.g oauth, OpenId connect)
- integrate app-specific user-credential checks
- role-based authorization
- optionally uses Clojure's ad-hoc hierarchies to model hierarchical roles
sucapabilities (a.k.a. "log in as"), enabling users to maintain multiple simultaneous logins, as well as to allow administrators to take on users' identities for debugging or support purposes (in progress)- and the creature comforts:
- Ring middlewares for configuring and defining the scopes of authentication and authorization
- Macros to clearly demarcate the scope of authentication and authorization within code that is "below" the level of Ring handlers where you can't use middlewares.
- A reasonable Clojure API around the jbcrypt library for hashing sensitive bits.
- Enables DRY routes and configuration, e.g. no need to configure your routes in Compojure or Noir or Moustache, and separately specify which routes fall under the jurisdiction of Friend's security machinery
- Purely functional in nature: authentications, roles, and session data are obtained, retained, and passed around as good ol' persistent data structures (just as Ring intended). No stateful session or context is ever bashed in place, making it easier to reason about what's going on.
Why?
Nothing like Friend exists, and it needs to. Securing Ring applications and services is (charitably speaking) a PITA right now, with everyone rolling their own, or starting with relatively low-level middlewares and frameworks. This will never do. Serious web applications need to take security seriously, and need to readily interoperate with all sorts of authentication mechanisms that have come to litter the web as well as internal networks.
Friend has been built with one eye on a number of frameworks.
- warden
- spring-security
- everyauth
- omniauth
- sandbar (defunct)
Status
Very stable, widely-used in production AFAIK.
Note: while actively maintained, Friend is in search of a new maintainer.
Changelog
Available here.
Known issues
- This README is way too long and not well-organized. It's more of a brain-dump than anything else at the moment.
- Configuration keys need a bit of tidying, especially for those that can/should apply to multiple authorization workflows. Fixes for such things will break the existing API.
- the
sumechanism is in-progress - …surely there's more. File issues.
"Installation"
Friend is available in Clojars. Add this :dependency to your Leiningen
project.clj:
[com.cemerick/friend "0.2.3"]
Or, add this to your Maven project's pom.xml:
<repository>
<id>clojars</id>
<url>http://clojars.org/repo</url>
</repository>
<dependency>
<groupId>com.cemerick</groupId>
<artifactId>friend</artifactId>
<version>0.2.3</version>
</dependency>
Friend is compatible with Clojure 1.2.0 - 1.5.0+.
Usage
How you use Friend will vary, sometimes significantly, depending on the authentication providers you use and the authorization policy/ies you want to enforce. A generic example of typical usage of Friend is below, but the best way to become familiar with Friend and how it can be used would be to go check out
http://friend-demo.herokuapp.com
…a collection of tiny demonstration apps using Friend. It should be easy to find the one(s) that apply to your situation, and go straight to its source so you can see how all the pieces fit together.
Here's probably the most self-contained Friend usage possible:
(ns your.ring.app
(:require [cemerick.friend :as friend]
(cemerick.friend [workflows :as workflows]
[credentials :as creds])))
; a dummy in-memory user "database"
(def users {"root" {:username "root"
:password (creds/hash-bcrypt "admin_password")
:roles #{::admin}}
"jane" {:username "jane"
:password (creds/hash-bcrypt "user_password")
:roles #{::user}}})
(def ring-app ; ... assemble routes however you like ...
)
(def secured-app
(-> ring-app
(friend/authenticate {:credential-fn (partial creds/bcrypt-credential-fn users)
:workflows [(workflows/interactive-form)]})
; ...required Ring middlewares ...
))
We have an unadorned (and unsecured) Ring application (ring-app, which
can be any Ring handler), and then the usage of Friend's authenticate
middleware. This is where all of the authentication work will be done,
with the return value being a secured Ring application (secured-app),
the requests to which are subject to the configuration provided to
authenticate and the authorization contexts that are defined within
ring-app (which we'll get to shortly).
(If you're newer to Clojure, you might not recognize the tokens prefixed with
two colons [e.g. ::admin]. These are auto-namespaced keywords; in the example
above, ::admin expands to :your.ring.app/admin.)
Authentication
There are two key abstractions employed during authentication:
workflow
and
credential
functions. The example above defines a single workflow — one supporting
the POSTing of :username and :password parameters to (by default)
/login — which will discover the specified :credential-fn and use it
to validate submitted credentials. The bcrypt-credential-fn function
verifies a submitted map of {:username "..." :password "..."}
credentials against one loaded from another function based on the
:username value; in this case, we're just looking up the username in a
fixed Clojure map that has username, (bcrypted) password, and roles
entries. If a submitted set of credentials matches those in the
authoritative store, the latter are returned (sans :password) as an
authentication map.
(Each workflow can have its own local configuration — including a
credential function — that is used in preference to the configuration
specified at the authenticate level.)
The authenticate middleware runs every incoming request through each
of the workflows with which it is created. It further handles things
like retaining authentication details in the user session (by default)
and managing the redirection of users when they attempt to access
protected resources without the requisite authentication or
authorization (first to the start of an authentication workflow, e.g.
GET of a /login URI, and then back to the originally-requested
protected resource once the authentication workflow is completed).
(Note that Friend itself requires some core Ring middlewares: params,
keyword-params and nested-params. Most workflows will additionally
require session in order to support post-authentication redirection to
previously-unauthorized resources, retention of tokens and nonces for
workflows like OpenId and oAuth, etc. HTTP Basic is the only provided
workflow that does not require session middleware.)
Workflows
Individual authentication methods (e.g., form-based auth, HTTP Basic, OpenID, oAuth, etc.) are implemented as workflows in Friend. A workflow is a regular Ring handler function, except that a workflow function can opt to return an authentication map instead of a Ring response if a request is authenticated. A diagram may help:

You can define any number of workflows in a :workflows kwarg to
authenticate. Incoming requests are always run through the configured
workflows prior to potentially being passed along to the secured Ring
application.
If a workflow returns an authentication map, then the authenticate
middleware will either:
- carry on processing the request if the workf
Related Skills
node-connect
339.1kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
83.8kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
339.1kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
83.8kCommit, push, and open a PR
