diff --git a/README.md b/README.md index 6d1cd4b..6809964 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![npm](https://img.shields.io/npm/v/couchdb-auth-proxy.svg)](https://www.npmjs.com/package/couchdb-auth-proxy) [![David](https://img.shields.io/david/tyler-johnson/couchdb-auth-proxy.svg)](https://david-dm.org/tyler-johnson/couchdb-auth-proxy) [![Build Status](https://travis-ci.org/tyler-johnson/couchdb-auth-proxy.svg?branch=master)](https://travis-ci.org/tyler-johnson/couchdb-auth-proxy) -An HTTP reverse proxy server for easy CouchDB proxy authentication. +A Node.js HTTP reverse proxy library for quick and dirty CouchDB proxy authentication. ## Install @@ -24,19 +24,14 @@ const couchdbProxy = require("couchdb-auth-proxy"); ## Usage -To begin, ensure proxy authentication is enabled on your CouchDB server. This is as simple as adding `{couch_httpd_auth, proxy_authentication_handler}` to the list of active authentication handlers in your configuration. Your `local.ini` file should have a line that looks something like this: +> Note: Ensure proxy authentication is enabled on your CouchDB server. This is as simple as adding `{couch_httpd_auth, proxy_authentication_handler}` to the list of active authentication handlers in your configuration. See the [CouchDB Docs](http://docs.couchdb.org/en/1.6.1/api/server/authn.html#proxy-authentication) for more info. -```ini -[httpd] -authentication_handlers = {couch_httpd_oauth, oauth_authentication_handler}, {couch_httpd_auth, cookie_authentication_handler}, {couch_httpd_auth, proxy_authentication_handler}, {couch_httpd_auth, default_authentication_handler} -``` +This library generates an HTTP server request function from two arguments: a user context method and some options. This method will work with Express/Connect apps as well as the plain Node.js HTTP server. -This library returns an Express/Connect middleware function. It accepts two arguments: a user context method and some options. Here is an example proxy that makes every request a super admin request. +Here is an example proxy that authenticates every request as a super admin: ```js -const app = express(); - -app.use(couchdbProxy(function(req) { +const server = http.createServer(couchdbProxy(function(req) { // admin party! return { name: null, @@ -45,30 +40,15 @@ app.use(couchdbProxy(function(req) { })); ``` -In CouchDB, users are represented with a user context object. These are simply objects with `name` and `roles` fields. Usually this information comes from a document in the `_users` database, however we can also generate it from other means. - -This library allows you to complete asynchronous authentication lookups. The simple version is to return a Promise. - -```js -app.use(couchdbProxy(function(req, res) { - const token = req.get("Authorization"); - - return validateToken(token).then(function(user) { - return { - name: user.name, - roles: [] - }; - }); -})); -``` +In CouchDB, users are represented with a user context object. These are objects with `name` and `roles` fields. Usually this information comes from a document in the `_users` database, however we can also generate it from other means. -Of course, you can also go with the more traditional `next` style callback that Express uses. If you do use the `next()` callback, you absolutely must call it, or the request will never complete. +Your proxy can complete asynchronous tasks, great for authenticating against other databases or services. You can return a promise, or provide a third argument for a callback. ```js -app.use(couchdbProxy(function(req, res, next) { +const server = http.createServer(couchdbProxy(function(req, res, next) { const token = req.get("Authorization"); - validateToken(token, function(err, user) { + db.authenticateToken(token, (err, user) => { if (err) return next(err); next(null, { @@ -89,10 +69,11 @@ app.use(couchdbProxy(function(req, res, next) { - `options.secret` (String) - The [CouchDB secret](http://docs.couchdb.org/en/1.6.1/config/auth.html#couch_httpd_auth/secret) used to sign proxy tokens and cookies. This is very much an optional parameter and in general there is very little reason to use a secret. This is only absolutely required if `couch_httpd_auth/proxy_use_secret` is enabled on CouchDB. - `options.via` (String) - The name of the proxy to add to the `Via` header. This is so consumers of the HTTP API can tell that the request was directed through a proxy. This is optional and the `Via` header will be excluded when not provided. - `options.headerFields` (Object) - A map of custom header fields to use for the proxy. This should match what is declared in CouchDB `couch_httpd_auth` configuration, under `x_auth_roles`, `x_auth_token`, and `x_auth_username`. This is the default map: - ```json - { - "username": "X-Auth-CouchDB-UserName", - "roles": "X-Auth-CouchDB-Roles", - "token": "X-Auth-CouchDB-Token" - } - ``` + ```json + { + "username": "X-Auth-CouchDB-UserName", + "roles": "X-Auth-CouchDB-Roles", + "token": "X-Auth-CouchDB-Token" + } + ``` + - `options.info` (Object) - Some JSON serializable value that will be injected into the CouchDB's root info document response. diff --git a/package.json b/package.json index 15f22d0..a8a07ea 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "couchdb-auth-proxy", "version": "0.0.0-edge", - "description": "An HTTP reverse proxy server for easy Couchdb proxy authentication", + "description": "An HTTP reverse proxy library for quick and dirty Couchdb proxy authentication", "author": "Tyler Johnson ", "repository": { "type": "git", diff --git a/src/index.js b/src/index.js index 1eb50b8..40700e8 100644 --- a/src/index.js +++ b/src/index.js @@ -1,15 +1,9 @@ -import crypto from "crypto"; +import {createHmac} from "crypto"; import {name,version} from "../package.json"; import {parse} from "url"; import httpProxy from "http-proxy"; import transformerProxy from "transformer-proxy"; -const injectProxyInfo = transformerProxy(function(data) { - const body = JSON.parse(data); - body.proxy = {name,version}; - return JSON.stringify(body); -}); - export default function(fn, opts={}) { if (typeof fn === "object") [opts,fn] = [fn,opts]; @@ -17,7 +11,8 @@ export default function(fn, opts={}) { via, secret, target="http://localhost:5984", - headerFields = {} + headerFields = {}, + info = { name, version } } = opts; headerFields = Object.assign({ @@ -26,12 +21,18 @@ export default function(fn, opts={}) { token: "X-Auth-CouchDB-Token" }, headerFields); + const injectProxyInfo = info ? transformerProxy(function(data) { + const body = JSON.parse(data); + body.proxy = info; + return JSON.stringify(body); + }) : null; + const proxy = httpProxy.createProxyServer({}); return async function(req, res, next) { try { // hijack the root response and inject proxy information - if (parse(req.url).pathname === "/") { + if (injectProxyInfo && parse(req.url).pathname === "/") { await confusedAsync(injectProxyInfo, null, [req, res]); } @@ -60,14 +61,15 @@ export default function(fn, opts={}) { proxy.web(req, res, { target }); } catch(e) { - next(e); + if (next) next(e); + else throw e; } }; } // couchdb proxy signed token function signRequest(user, secret) { - return crypto.createHmac("sha1", secret).update(user).digest("hex"); + return createHmac("sha1", secret).update(user).digest("hex"); } // for methods that we don't know if they are callback or promise async