├── .gitignore ├── .travis.yml ├── README.md ├── index.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | .DS_Store 3 | node_modules/ 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.12 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CouchDB OAuth example 2 | 3 | [![Build Status](https://travis-ci.org/lupomontero/couchdb-oauth.svg?branch=master)](https://travis-ci.org/lupomontero/couchdb-oauth) 4 | [![Dependency Status](https://david-dm.org/lupomontero/couchdb-oauth.svg?style=flat)](https://david-dm.org/lupomontero/couchdb-oauth) 5 | [![devDependency Status](https://david-dm.org/lupomontero/couchdb-oauth/dev-status.png)](https://david-dm.org/lupomontero/couchdb-oauth#info=devDependencies) 6 | 7 | This repo contains an example script that aims to show how to use [CouchDB](http://couchdb.apache.org/)'s OAuth authentication. CouchDB has support for 2-legged OAuth 1.0, but do note that it does not handle the creation of OAuth credentials (consumer keys, consumer secretes, tokens and token secrets). 8 | 9 | The script does the following: 10 | 11 | 1. Spins up a CouchDB server using [MultiCouch](https://github.com/hoodiehq/node-multicouch). 12 | 2. Configures CouchDB server to store OAuth credentials in `_users` database. 13 | 3. Creates a user with OAuth credentials in its user document. 14 | 4. Sends request to CouchDB server using [OAuth](https://github.com/ciaranj/node-oauth) to prove authentication works. 15 | 5. Stops the CouchDB server and cleans up. 16 | 17 | ## Install 18 | 19 | ``` 20 | git clone https://github.com/lupomontero/couchdb-oauth.git 21 | cd couchdb-oauth 22 | npm install 23 | ``` 24 | 25 | ## Run 26 | 27 | ``` 28 | npm test 29 | ``` 30 | 31 | This should produce output similar to: 32 | 33 | ``` 34 | > couchdb-oauth@1.0.0 test /Users/lupo/Documents/workspace/lupomontero/couchdb-oauth 35 | > node index.js 36 | 37 | { ok: true, 38 | userCtx: { name: 'lupo', roles: [] }, 39 | info: 40 | { authentication_db: '_users', 41 | authentication_handlers: [ 'oauth', 'cookie', 'default' ], 42 | authenticated: 'oauth' } } 43 | ``` 44 | 45 | ## Further reading 46 | 47 | ### CouchDB Docs 48 | 49 | * [OAuth Authentication](http://docs.couchdb.org/en/latest/api/server/authn.html#oauth-authentication) 50 | * [HTTP OAuth Configuration](http://docs.couchdb.org/en/1.6.1/config/auth.html#http-oauth-configuration) 51 | * [OAuth Configuration](http://docs.couchdb.org/en/1.6.1/config/auth.html#oauth-configuration) 52 | 53 | ### CouchDB Mailing List Archives 54 | 55 | * Feb 2013: [Re: Help! 2-legged OAuth Example Anyone?](http://mail-archives.apache.org/mod_mbox/couchdb-user/201302.mbox/%3CCADR1q3BLuVwCuhvEFL0nYjM9D6PrYzUsd_wg9eBHp9VdZ0Pk=A@mail.gmail.com%3E) 56 | * Nov 2010: [OAuth example](http://grokbase.com/t/couchdb/user/10b28b0fv4/oauth-example) 57 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var os = require('os'); 2 | var fs = require('fs'); 3 | var path = require('path'); 4 | var util = require('util'); 5 | var assert = require('assert'); 6 | var rimraf = require('rimraf'); 7 | var MultiCouch = require('multicouch'); 8 | var request = require('request'); 9 | var OAuth = require('oauth').OAuth; 10 | 11 | 12 | // Data dir for the CouchDB instance that we'll start with multicouch. 13 | var dataDir = path.join(os.tmpdir(), 'couchdb-oauth-test'); 14 | rimraf.sync(dataDir); // Remove if already exists 15 | fs.mkdirSync(dataDir); // Create data dir 16 | 17 | 18 | // Use multicouch to spin up a CouchDB instance. 19 | var couchUrl = 'http://127.0.0.1:55984'; 20 | var couch = new MultiCouch({ 21 | port: 55984, 22 | prefix: dataDir 23 | }); 24 | 25 | 26 | // A dummy user to test OAuth authentication. 27 | var userDocUrl = couchUrl + '/_users/org.couchdb.user:lupo'; 28 | var userDoc = { 29 | name: 'lupo', 30 | password: 'supersecret', 31 | roles: [], 32 | type: 'user', 33 | oauth: { 34 | consumer_keys: { 35 | consumerKey1: 'consumerKeySecret1' 36 | }, 37 | tokens: { 38 | token1: 'tokenSecret1' 39 | } 40 | } 41 | }; 42 | 43 | 44 | // Start CouchDB and wait for it to be accepting connections. 45 | function startCouch() { 46 | var retries = 10; 47 | function wait() { 48 | request(couchUrl, function (err) { 49 | if (err) { 50 | if (!retries--) { return done(err); } 51 | return setTimeout(wait, 1000); 52 | } 53 | configureCouch(); 54 | }); 55 | } 56 | couch.on('start', wait); 57 | couch.start(); 58 | } 59 | 60 | 61 | // Configure CouchDB to use _users database to store OAuth credentials. 62 | function configureCouch() { 63 | request.put(couchUrl + '/_config/couch_httpd_oauth/use_users_db', { 64 | json: true, 65 | body: 'true' 66 | }, function (err, resp) { 67 | if (err) { return done(err); } 68 | createUser(); 69 | }); 70 | } 71 | 72 | 73 | // Create dummy user. 74 | function createUser() { 75 | request.put(userDocUrl, { 76 | json: true, 77 | body: userDoc 78 | }, function (err, resp) { 79 | if (err) { return done(err); } 80 | userDoc._rev = resp.body.rev; 81 | checkOAuth(); 82 | }); 83 | } 84 | 85 | 86 | // Test that we can authenticate the dummy user using OAuth 1.0. 87 | function checkOAuth() { 88 | var oauth = new OAuth( 89 | couchUrl + '/_oauth/request_token', 90 | couchUrl + '/_oauth/access_token', 91 | 'consumerKey1', 92 | 'consumerKeySecret1', 93 | '1.0', 94 | null, 95 | 'HMAC-SHA1' 96 | ); 97 | oauth.get( 98 | couchUrl + '/_session', 99 | 'token1', 100 | 'tokenSecret1', 101 | function (err, data) { 102 | if (err) { return done(err); } 103 | var parsed = JSON.parse(data); 104 | assert.deepEqual(parsed, { 105 | ok: true, 106 | userCtx: { name: 'lupo', roles: [] }, 107 | info: { 108 | authentication_db: '_users', 109 | authentication_handlers: [ 'oauth', 'cookie', 'default' ], 110 | authenticated: 'oauth' 111 | } 112 | }); 113 | console.log(util.inspect(parsed, { colors: true })); 114 | removeUser(); 115 | }); 116 | } 117 | 118 | 119 | // Remove dummy user. 120 | function removeUser() { 121 | request.del(userDocUrl, { 122 | json: true, 123 | qs: { rev: userDoc._rev }, 124 | auth: { user: userDoc.name, pass: userDoc.password } 125 | }, stopCouch); 126 | } 127 | 128 | 129 | function stopCouch() { 130 | couch.stop(done); 131 | } 132 | 133 | 134 | function done(err) { 135 | rimraf.sync(dataDir); 136 | if (err) { 137 | console.error(util.inspect(err)); 138 | process.exit(1); 139 | } 140 | process.exit(0); 141 | } 142 | 143 | 144 | startCouch(); // Start the action... 145 | 146 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "couchdb-oauth", 3 | "version": "1.0.0", 4 | "scripts": { 5 | "test": "node index.js" 6 | }, 7 | "dependencies": { 8 | "multicouch": "^0.8.2", 9 | "oauth": "^0.9.12", 10 | "request": "^2.55.0", 11 | "rimraf": "^2.3.2" 12 | } 13 | } 14 | --------------------------------------------------------------------------------