Using a Cloudant API Key with Multiple Cloudant Databases and Accounts

If you use Cloudant’s dashboard to generate API keys and assign permissions, you’d be forgiven for thinking that an API key can only be used for one database. However, that’s just an unfortunate implication of the UI. In fact, you can give an API key permission to read or write any number of databases – even ones on different Cloudant accounts.

The key to doing this is to use Cloudant’s HTTP API rather than the dashboard. Here’s how.

1. Generate an API key

First, generate an API key using your Cloudant account’s admin credentials:

curl -XPOST -u mikerhodes 'https://mikerhodes.cloudant.com/_api/v2/api_keys'
{
  "password": "1dd24951d0cde9839abc094c1c49d49965908d23", 
  "ok": true, 
  "key": "blemanitillyindstooksong"
}

This key to understanding what we’re about to do is to think of this API key as a lightweight Cloudant user. Unlike an account, this user doesn’t have its own databases, but instead it can be granted permissions on any other database within Cloudant.

The key and password in this example are, of course, made up.

2. Assign the API key permissions on database(s)

Next, assign the API key permissions on a database. Permissions define what a request bearing that API key can do, for example the _reader permission allows reading documents amongst other things.

We’ll start with a database on the mikerhodes account where we generated the API key, animaldb. To do this, you need to retrieve the database’s _security document and modify it. First, get it:

curl -u mikerhodes 'https://mikerhodes.cloudant.com/animaldb/_security'
{
  "cloudant": {
    "bouninamendouldnimendepa": [
      "_reader"
    ],
    "mikerhodes": [
      "_reader",
      "_writer",
      "_admin",
      "_replicator"
    ],
    "nobody": []
  },
  "_id": "_security"
}

Let’s pull apart this security document:

  1. The cloudant field is where you assign permissions to Cloudant API keys and users. The sub-fields are keyed by username (or API key), and have an array of permissions for that user.
  2. The nobody field ensures that anonymous users have no permissions.
  3. The mikerhodes field grants my admin account all permissions. This is a bit redundant as the account admin gains all permissions on all databases by default.
  4. The bouninamendouldnimendepa is an API key I’ve already granted _reader permissions to.

To assign new permissions, add the new API key’s username/key to the cloudant field along with the permissions we want to give it. Here’s how to grant it _reader and _replicator:

curl -XPUT -d @- -u mikerhodes 'https://mikerhodes.cloudant.com/animaldb/_security'
{
  "cloudant": {
    "bouninamendouldnimendepa": [
      "_reader"
    ],
    "blemanitillyindstooksong": [
      "_reader", "_replicator"
    ],
    "mikerhodes": [
      "_reader",
      "_writer",
      "_admin",
      "_replicator"
    ],
    "nobody": []
  },
  "_id": "_security"
}

### hit ctrl-d *twice* to terminate and send the input ###

{"ok": true}

Note: here I use the -d @- option to get curl to read from stdin. This means you can just paste the new _security document, then hit ctrl-d twice to terminate and send the request body.

Here we’ve added blemanitillyindstooksong to the user list. Check using a GET to the security document to make sure it worked.

An interesting note is that for currently updates to _security you don’t need to supply a _rev along with the request as you would with normal document updates. This is because the _security document is unversioned as it’s never replicated. There have been a few thoughts around starting to require a _rev in future releases, so keep alert in case it does change.

3. Make requests using the API key

Now blemanitillyindstooksong can make requests to the database. First, let’s check the anonymous user really can’t access the database:

> curl 'https://mikerhodes.cloudant.com/animaldb'
{"error":"unauthorized","reason":"_reader access is required for this request"}

And now show blemanitillyindstooksong can:

> curl -u blemanitillyindstooksong 'https://mikerhodes.cloudant.com/animaldb' 
{
  "db_name": "animaldb",
  [ ... ]
}

4. Grant the API key permissions on other databases

The key here is that the steps for other databases are exactly the same as above, so go through steps (2) and (3) to grant the API key access to more databases, using whatever combination of permissions you require.

More ways to use API keys and permissions

There are less obvious ways that API keys and permissions can be used. The main two are:

  • You can grant permissions to an API key for databases hosted on accounts other than the one used to generate the API key.
  • You can grant permissions to another Cloudant user for a database on your account.

Granting permissions to an API key generated on a different Cloudant account

Here, I take the API key we generated above using a request to the mikerhodes account and I grant it permissions on a database on another of my accounts, mikerhodesporter:

> curl 'https://mikerhodesporter.cloudant.com/testuserpost/_security' \
    -XPUT -u mikerhodesporter -d @-
{
  "_id": "_security",
  "cloudant": {
    "nobody": [],
    "blemanitillyindstooksong": [
      "_reader",
      "_replicator"
    ]
  }
}{"ok":true}

And now I can use that API key to access the database:

> curl -u blemanitillyindstooksong \
    'https://mikerhodesporter.cloudant.com/testuserpost'
{
  "db_name": "testuserpost",
  [ ... ]
}

Allow different Cloudant accounts access to your databases

Here I grant my account mikerhodesporter access to a database on my mikerhodes account. This allows the owner of the mikerhodesporter account to access data in the animaldb database on my mikerhodes account.

Initially, I, of course, cannot use mikerhodesporter to access the database:

> curl -u mikerhodesporter 'https://mikerhodes.cloudant.com/animaldb'
{"error":"forbidden","reason":"_reader access is required for this request"}

So I update the security for the database using my mikerhodes account:

> curl 'https://mikerhodes.cloudant.com/animaldb/_security' \
    -XPUT -u mikerhodes -d @-
{
  "_id": "_security",
  "cloudant": {
    [ ... ]
    "mikerhodesporter": [
      "_reader",
      "_writer"
    ],
    [ ... ]
  }
}{"ok":true}

And now I’m able to access the database using the mikerhodesporter account credentials:

> curl -u mikerhodesporter 'https://mikerhodes.cloudant.com/animaldb'
{
  "db_name": "animaldb",
  [ ... ]
}

Recap

This was a bit of a whirlwind tour through Cloudant’s permissions and user mechanism. What are the key points?

  • An API key can be given permissions on any database within Cloudant, unlike what the dashboard implies.
  • A Cloudant account can be given permissions on any database within Cloudant using the same tools. This underlies Cloudant’s sharing functionality, which layers UI on top of these building blocks.

Broadly, Cloudant account and API key credentials are universal in that they can be used across the service to grant access to databases.

.:.