What’s new in CouchDB 1.0 — Part 4: Security’n stuff: Users, Authentication, Authorisation and Permissions
Welcome to Part 4 on my little mini-series on new features in CouchDB 0.11.01.0.
Do not miss parts one, two and three.
Today, I get a little help from Rebecca. She’s writing a CouchApp, an
application that is served right out of CouchDB and that lives in the browser.
It has no middle tier application server in Ruby or Java. The application and
display logic is written in JavaScript, the user interface is HTML & CSS, the
backend is CouchDB and uses Ajax to shove JSON back and forth.
Rebecca is writing a small todo list app for herself and her friends, but
we’ll punt on the actual application for now, so we can concentrate on the
security features. Part two and three of our book CouchDB: The Definitive
Guide explains how the rest of the application development works, make
sure to read up on it!
Security
Security is a wide field. This article only discusses some of things you need
to know to add authenticated-user features to your CouchDB application
(whether it is a CouchApp or a regular application). It does not discuss best
practices for securing network servers or defending against
cross site scripting.
View Source is Open Source
The CouchDB security model is based around the premise that Rebecca can
control who can create documents of what form into which database inside
CouchDB. It does not try to make CouchDB and all the data she and others put
in is absolutely water-tight and doesn’t leak any information. Although you
can lock CouchDB as much down as you need, open and sharable databases
are the default and it is a good thing.
Traditional applications are built with a database in the back; an application
is the only logical “user” of a database, the only entity that accesses the
database directly (aside from maybe an administrator). CouchDB happily
supports that model (there is nothing wrong with it either). The only case
where security is relevant here is shared hosting where you have multiple
mutually untrusting parties accessing a single CouchDB instance. The mechanics
I describe can be used to make CouchDB useful here, but I won’t describe this
specific scenario. Partly because it is a lot simpler to just give every user
a separate full instance of CouchDB with “root” access, but mostly because I
believe there is a much more interesting deployment scenario:
The idea of standalone CouchApps is that they travel with the data, since they
are just some HTML, CSS & JavaScript that CouchDB tack onto a _design
document as attachments. Applications as data allow us to replicate them
around just like we do with data. Data ultimately wants to be free and
shareable with the people and applications we trust. Why shouldn’t
applications do the same?
Since CouchApps run in the browser you can’t hide their implementation
anywhere. In that, CouchApps are inherently Open Source and we believe that is
a good thing because that is how the web works and that’s a crucial feature of
the web. View source allows everyone curious to learn how a website was
built.
Yadda, yadda, Open Source zealottery, you can’t hear it anymore, sorry :) — If
you like your Rails, Django, PHP or Java, CouchDB won’t prevent you from using
them. You can create private, closed source applications, but you’re losing
the powerful attribute of native app-shareability.
All that said you can make CouchDB as closed as you need it, but with each
barrier to entry you lose a layer of data and application flexibility. You
might want to reconsider some of your previous ideas about how to lock down
your app in the light of ultra-portable, peer-to-peer shareable applications.
Ok, CouchApps are a big deal, you get that now, I’ll shut up. Back to the nuts
and bolts of CouchDB security :)
Terminology
Lets make sure we talk about the same things.
Admin Party: CouchDB by default comes in the admin party mode. Each request
made to CouchDB is considered to come from an admin. This means it is
extremely easy to get started. Isn’t that terribly insecure, you ask. By
default CouchDB will only listen on 127.0.0.1, your localhost IP address.
Only users on your computer can access CouchDB. Most of the time that‘s just
you, so no biggie; but be aware of this when you are on machine with multiple,
possibly untrusting users.
Database: A database is a bucket that holds any number of documents in
CouchDB. Each CouchDB server can have any number of databases. Each database
is self contained and access to CouchDB can be defined on a per-database
level. I’ll show you how below.
User: A user is identified by a username and matching password that is
securely stored inside CouchDB. A user can have one or more roles assigned.
A user with an empty name and password is the anonymous user. CouchDB
further distinguishes admin users between server admins and database admins.
Authentication: The process of a user proving it’s her by providing the
correct username / password combination in an authenticated HTTP request.
Authorisation: The process of determining whether an authenticated user is
allowed to do what she wants to do.
Roles: Roles are associated with users, you could also call them “group”.
For example, in Admin Party mode, each request is implicitly authenticated
with the anonymous user that in turn implicitly gets assigned the _admin
role that allows each request to do anything.
Anonymous User: A user with an empty username and password. All
unauthenticated requests are implicitly assigned to the anonymous user.
Access Control Lists: A list of usernames or roles for a database. CouchDB
distinguishes reader-ACLs and admin-ACLs. A database admin can fully
access the database. The reader-ACL list defines a list of users or roles that
can read from the database. If no reader-ACLs are defined, everybody can read
from the database. Note that there is no writer-ACL; see Validation
Functions next.
Validation Functions: A JavaScript function stored in the
validate_doc_update field of a _design document. It gets executed whenever
a write requests reaches the database. It can decide to allow or deny access
to the database based on the document that is being written and the
authenticated user or her roles.
Stateless HTTP: Each HTTP requests stands on its own. A client and server do
not expect any previous requests have be made.
Basic Auth: An authentication mechanism for HTTP that uses base64 encoded
headers to send a users credentials to the server. Most notably known for
producing an ugly pop-up window in the browser (although this can be
prevented). Base64 encoding is not a form of encryption. It is not a safe
transport for user credentials. It is easy for third parties to spy out a
user’s password. Basic auth can only reasonably used in a trusted environment;
a local LAN, a VPN or over SSL.
Secure Cookie Auth: Unlike Basic Auth Secure Cookie Auth uses
HMAC-encryption for transporting user credentials. It can be used to
securely authenticate users over an untrusted connection.
OAuth: Lets users allow applications to authenticate as the user to a
service. The canonical example is a web application that does something with a
user’s private account data on another service. With OAuth, the web
application does not have to know the user’s credentials to do its work.
Access permissions can be managed and revoked on a per-application basis.
OAuth is not limited to web applications though.
Getting Started with Security in Futon
Let’s start with a blank slate. A fresh installation of CouchDB 0.11.0 or
later, the admin party and a look at Futon:
In the lower right you should see that in fact, we are having an admin party.

You should also see a link that asks you to “Fix This”. Click it and you
should see a form that asks you to specify a username and password for the
first server admin user.

I put in rebecca and 12345, you can choose whatever else you like. Just
remember it, otherwise, you’re locked out of the server (there are ways to get
in again, but that’s beyond the scope of this article.)
The lower right should now greet you with your username and offer you to
create more admins or to log out. Futon uses Secure Cookie Authentication to
keep you logged in.

At this point, CouchDB no longer runs in Admin Party mode and requires you to
be logged in to perform certain actions: Creating or deleting a database;
creating, updating or deleting a _design document inside a database; read or
update the _config API; read _stats or _log and request temporary views.
Under the Hood
Let’s see what goes on under the hood of Futon. We’re following the same steps
as above, only we use curl on the command line instead of Futon.
First, see if CouchDB is running:
> curl http://127.0.0.1:5984/
{"couchdb":"Welcome","version":"1.0.0"}
Yes!
Next, let’s create an admin user:
> curl -X PUT http://127.0.0.1:5984/_config/admins/rebecca -d’"12345"’
""
Well that was easy. We created a config-level admin user and that takes
CouchDB right out of admin party mode (intentionally left over production
note: reference “partypooper” in some way).
Now all administrative requests to CouchDB (see above) need to be
authenticated. To make your life a little easier Futon does a little dance for
you and automatically logs you in as the newly created user.
What does logs you in mean? First, Futon creates a new document in the
_users database. It has a special format that you have to follow if you
are doing this on your own.
Luckily CouchDB’s buit-in client libraries couch.js and jquery.couch.js
do all the heavy lifting for you.
> cat rebecca.json
{
"_id":"org.couchdb.user:rebecca",
"name": "rebecca",
"salt": "68cf5946d9760d19759b5016d90f612c",
"password_sha": "3588a9b2039e53b674d8da361e4be98f00637f5a",
"type":"user",
"roles":["admin"]
}
> curl -X PUT "http://127.0.0.1:5984/_users/org.couchdb.user%3Arebecca" \
-d@rebecca.json
{
"ok":true,
"id":"org.couchdb.user:rebecca",
"rev":"1-9aa9e9a2c855e81061d6d8553d6adbc5"
}
This is laying foundations for the future. Once you have a user document in
the _users database, you can use the _session API to get an encrypted
session cookie that authenticates you for the next few requests. By default, a
session cookie is valid for 10 minutes.
Showing all the cookie business with curl would be a little tedious, so
I’ll jump over to how to do all that in your own code.
Aside: What’s the deal with users or admins created with the _config API vs.
the _users database? Admins created through the _config API are persisted
to CouchDB’s configuration file ($prefix/etc/couchdb/local.ini by default).
Users in the _users database are stored in that database. For some setups,
it is required that some external tool is able to create a user for CouchDB
without having a user or admin account on that CouchDB, but access to the
ini file (think system setup software). In addition, _config users are
always automatically server admins, so use them with care.
Using jquery.couch.js in Your Application
jquery.couch.js is the standard JavaScript API that ships with CouchDB.
Futon uses it for its snazzy interface. And so can you, or should, really,
unless you want to re-do all the work the CouchDB project put into it :)
Let me show you the methods in question. I’m quoting right out of the API
docs:
$.couch.signup(user_doc, password, options)
- Hashes the password
- Adds an empty roles array to the user_doc when not specified
- Adds an id, composed of “org.couchdb.user:” and name, to the userdoc when not specified
- Saves the user_doc with options as parameters in the userDb
- Performs the success callback on the saved user_doc
$.couch.login(options)
- Does a POST request to “_session” with username and password, they have to be present in the options hash. Throws a 404 error when the password is wrong or there is no user with that username stored in the userDb.
$.couch.logout(options)
- Does a DELETE request to “_session”.
Concepts
The _session API provides you with a convenient endpoint to manage
authenticated requests to CouchDB. A simple GET /_session returns a JSON
object detailing your current session state.
> curl http://127.0.0.1:5984/_session | jsonpretty
{
"userCtx": {
"name": null,
"roles": [
]
},
"info": {
"authentication_handlers": [
"oauth",
"cookie",
"default"
],
"authentication_db": "_users"
},
"ok": true
}
userCtx is where all the authentication information is stored. name is
your login username and roles is list of roles your user has assigned to it.
info has some server-wide information about the authentication system.
authentication_handlers are the different ways CouchDB can do the actual
authentication process for you. By default CouchDB ships with an OAuth
handler, a cookie handler and the default handler (which does HTTP basic
auth). The authentication_db is the database that user documents are stored
in. The default is _users, but you can change it in the CouchDB
configuration settings. Only do so if you have a very good reason.
ok just lets us know our request was a-ok.
We made an unauthenticated request to CouchDB, so we don’t see any values for
userCtx.name or userCtx.roles. Let’s make one with user credentials:
> curl http://rebecca:12345@127.0.0.1:5984/_session | jsonpretty
{
"userCtx": {
"name": "rebecca",
"roles": [
"_admin"
]
},
"info": {
"authentication_handlers": [
"oauth",
"cookie",
"default"
],
"authenticated": "default",
"authentication_db": "_users"
},
"ok": true
}
The result looks a lot similar, but this times the values inside userCtx are
filled out. We used HTTP basic auth. The details of OAuth authentication are
out of the scope of this article, but we sure should feature them at some
point.
Cookie authentication or it’s full name Secure Cookie Authentication works
by granting access through HMAC digest transported credentials and one time
tokens. To increase convenience, a one time token is actually valid for 10
minutes by default, but you can adjust that as needed.
We showed you the login() method of jquery.couch.js earlier, use it to
log a user into CouchDB with cookie authentication.
Roles
With roles you can group multiple users. We’ll show you in a bit how roles
allow you to define permissions on the CouchDB server and individual
databases. A role is a simple string that doesn’t start with an underscore.
Underscore-roles are reserved to CouchDB. You roles can be anything, really.
The only role that CouchDB prescribes is the _admin role (with an
underscore, see?). It grants the user server-wide privileges to do anything.
ACLs, Database Admins & Validation Functions
To allow more fine-grained control over who can read from your databases,
CouchDB comes with Access Control Lists (ACLs), Database Admins and
Validation Functions.
Each database in CouchDB comes with its own security object. It is not a
document, but simply a JSON structure associated with the database. On a newly created database, it looks like this:
{}
The empty object, duh :)
You can set two properties admins and readers. Both are another JSON
object with the two properties roles and names and these two are lists
of roles and names respectively.
Here is an example:
{
"admins": {
"roles": [],
"names": ["rebecca"]
}
}
For this database, our user rebecca is the admin. A database admin has
full read access to the database as well as the ability to update the security
object. You can add more users:
{
"admins": {
"roles": [],
"names": ["rebecca", "pete"]
}
}
Or if that starts to get tedious, you can assign roles, and then by adding
roles to a user, they automatically inherit the right to administer the
database. This assumes, “rebecca” and “pete” each have the “local-heros” role
assigned:
{
"admins": {
"roles": ["local-heroes"],
"names": []
}
}
Readers
Now this is awesome :) — You can specify in the same way a list of usernames
or roles to grant read-access to a database. If no readers are specified,
everyone can read your database. This is cool, again, public databases make
the world a better place.
In case you want only specific authenticated users to be able to read from
your database, use the security object:
{
"admins": {
"roles": ["local-heroes"],
"names": ["rebecca", "pete"]
},
"readers": {
"roles": ["lolcat-heroes"],
"names": ["simon", "ben", "james"]
}
}
Now simon, ben and james are among your trusted readers as well as all
users with the role “lolcat-heores”.
There is no need to add names or roles from the admins section, since they
automatically are also readers.
Validation Functions or How to Control Write Access
What about restricting write access, can you just create a new property
writers in the security object and do as before? — No, for this, you will
be using a validation function.
CouchDB has had validation functions for quite some time and always have been
the way of restricting write access to your database. The cool thing with
validation functions is that they have full access to the document a user is
trying to write as well as the user context, i.e. the username and any roles.
This allows a validation function to reject a document write because of both
user-authentication (or the lack thereof) and document content or structure.
Validation functions are invoked once for every document that is written to
the database. It gets passed the document to be written, the previous revision
of a document, if it exists, and the user context. To block a document write,
the validation function needs to throw an exception. The return value is
ignored. If no exceptions are thrown, the document write can proceed.
Here are a few examples.
Disallowing anonymous writes:
function(new_doc, old_doc, userCtx) {
if(!userCtx.name) {
// CouchDB sets userCtx.name only after a successful authentication
throw({forbidden: "Please log in first."});
}
}
Only allow writes to users with a certain role:
function(new_doc, old_doc, userCtx) {
if(userCtx.roles.indexOf("editors") === -1) {
// sure lovely that JavaScript doesn’t
// have an Array.includes() method
throw({unauthorized: "You are not an editor."});
}
}
Only allow updates by the author (this assumes, that the user sets his or her
username as doc.name).
function(new_doc, old_doc, userCtx) {
if(doc.name != userCtx.name) {
throw({unauthorized: "You are not the author"});
}
}
Conclusion
Alright, this was a really long post and we should get it wrapped. We hope to
have given you a good overview of the security concepts in CouchDB and enough
pointers to keep you reading and experimenting.