diff --git a/HISTORY.md b/HISTORY.md index 2c44a01..5eed317 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,3 +1,8 @@ +unreleased +================== + + * feat: add `auth.format` for formatting credentials + 2.0.1 / 2018-09-19 ================== diff --git a/README.md b/README.md index 85f1bac..989fe9c 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,11 @@ otherwise an object with `name` and `pass` properties. Parse a basic auth authorization header string. This will return an object with `name` and `pass` properties, or `undefined` if the string is invalid. +### auth.format(credentials) + +Format a credentials object with `name` and `pass` properties as a basic +auth authorization header string. + ## Example Pass a Node.js request object to the module export. If parsing fails @@ -60,6 +65,19 @@ var auth = require('basic-auth') var user = auth.parse(req.getHeader('Proxy-Authorization')) ``` +A credentials object can be formatted with `auth.format` as +basic auth header string. + + + + +```js +var auth = require('basic-auth') +var credentials = { name: 'foo', pass: 'bar' } +var authHeader = auth.format(credentials) +// => "Basic Zm9vOmJhcg==" +``` + ### With vanilla node.js http server ```js diff --git a/index.js b/index.js index 9106e64..134e2bb 100644 --- a/index.js +++ b/index.js @@ -22,6 +22,7 @@ var Buffer = require('safe-buffer').Buffer module.exports = auth module.exports.parse = parse +module.exports.format = format /** * RegExp for basic auth credentials @@ -122,6 +123,29 @@ function parse (string) { return new Credentials(userPass[1], userPass[2]) } +/** + * Format Basic Authorization Header + * + * @param {Credentials} credentials + * @return {string} + * @public + */ +function format (credentials) { + if (!credentials) { + throw new TypeError('argument credentials is required') + } + + if (typeof credentials !== 'object') { + throw new TypeError('argument credentials is required to be an object') + } + + if (typeof credentials.name !== 'string' || typeof credentials.pass !== 'string') { + throw new TypeError('argument credentials is required to have name and pass properties') + } + + return 'Basic ' + Buffer.from(credentials.name + ':' + credentials.pass).toString('base64') +} + /** * Object to represent user credentials. * @private diff --git a/test/basic-auth.js b/test/basic-auth.js index 96877f3..bf3f818 100644 --- a/test/basic-auth.js +++ b/test/basic-auth.js @@ -230,3 +230,74 @@ describe('auth.parse(string)', function () { }) }) }) + +describe('auth.format(credentials)', function () { + describe('arguments', function () { + describe('credentials', function () { + it('should be required', function () { + assert.throws(auth.format, /argument credentials is required/) + }) + + it('should accept credentials', function () { + var header = auth.format({ name: 'foo', pass: 'bar' }) + assert.strictEqual(header, 'Basic Zm9vOmJhcg==') + }) + + it('should reject null', function () { + assert.throws(auth.format.bind(null, null), /argument credentials is required/) + }) + + it('should reject a number', function () { + assert.throws(auth.format.bind(null, 42), /argument credentials is required/) + }) + + it('should reject a string', function () { + assert.throws(auth.format.bind(null, ''), /argument credentials is required/) + }) + + it('should reject an object without name', function () { + assert.throws(auth.format.bind(null, { pass: 'bar' }), /argument credentials is required to have name and pass properties/) + }) + + it('should reject an object without pass', function () { + assert.throws(auth.format.bind(null, { name: 'foo' }), /argument credentials is required to have name and pass properties/) + }) + + it('should reject an object with non-string name', function () { + assert.throws(auth.format.bind(null, { name: 42, pass: 'bar' }), /argument credentials is required to have name and pass properties/) + }) + + it('should reject an object with non-string pass', function () { + assert.throws(auth.format.bind(null, { name: 'foo', pass: 42 }), /argument credentials is required to have name and pass properties/) + }) + }) + }) + + describe('with valid credentials', function () { + it('should return header', function () { + var header = auth.format({ name: 'foo', pass: 'bar' }) + assert.strictEqual(header, 'Basic Zm9vOmJhcg==') + }) + }) + + describe('with empty password', function () { + it('should return header', function () { + var header = auth.format({ name: 'foo', pass: '' }) + assert.strictEqual(header, 'Basic Zm9vOg==') + }) + }) + + describe('with empty userid', function () { + it('should return header', function () { + var header = auth.format({ name: '', pass: 'pass' }) + assert.strictEqual(header, 'Basic OnBhc3M=') + }) + }) + + describe('with empty userid and pass', function () { + it('should return header', function () { + var header = auth.format({ name: '', pass: '' }) + assert.strictEqual(header, 'Basic Og==') + }) + }) +})