fastify-basic-auth
Advanced tools
Comparing version 1.0.1 to 1.1.0
51
index.js
@@ -7,5 +7,5 @@ 'use strict' | ||
function basicPlugin (fastify, opts, next) { | ||
async function basicPlugin (fastify, opts) { | ||
if (typeof opts.validate !== 'function') { | ||
return next(new Error('Basic Auth: Missing validate function')) | ||
throw new Error('Basic Auth: Missing validate function') | ||
} | ||
@@ -16,13 +16,8 @@ const authenticateHeader = getAuthenticateHeader(opts.authenticate) | ||
next() | ||
function basicAuth (req, reply, next) { | ||
if (authenticateHeader) { | ||
reply.header(authenticateHeader.key, authenticateHeader.value) | ||
} | ||
var credentials = auth(req) | ||
const credentials = auth(req) | ||
if (credentials == null) { | ||
done(new Unauthorized('Missing or bad formatted authorization header')) | ||
} else { | ||
var result = validate(credentials.name, credentials.pass, req, reply, done) | ||
const result = validate(credentials.name, credentials.pass, req, reply, done) | ||
if (result && typeof result.then === 'function') { | ||
@@ -33,4 +28,9 @@ result.then(done, done) | ||
function done (err) { | ||
if (err !== undefined) { | ||
function done (err, realm) { | ||
// TODO remove in the next major | ||
if (typeof err === 'string') { | ||
realm = err | ||
err = undefined | ||
} | ||
if (err) { | ||
// We set the status code to be 401 if it is not set | ||
@@ -42,2 +42,4 @@ if (!err.statusCode) { | ||
} else { | ||
const header = realm ? formatRealm(realm) : authenticateHeader | ||
reply.header('WWW-Authenticate', header) | ||
next() | ||
@@ -52,20 +54,25 @@ } | ||
if (authenticate === true) { | ||
return { | ||
key: 'WWW-Authenticate', | ||
value: 'Basic' | ||
} | ||
return 'Basic' | ||
} | ||
if (typeof authenticate === 'object') { | ||
const realm = (authenticate.realm && typeof authenticate.realm === 'string') | ||
? authenticate.realm | ||
: '' | ||
return { | ||
key: 'WWW-Authenticate', | ||
value: 'Basic' + (realm ? ` realm="${realm}"` : '') | ||
const realm = formatRealm(authenticate.realm) | ||
if (realm) { | ||
return realm | ||
} | ||
} | ||
throw Error('Basic Auth: Invalid authenticate option') | ||
throw new Error('Basic Auth: Invalid authenticate option') | ||
} | ||
function formatRealm (realm) { | ||
switch (typeof realm) { | ||
case 'undefined': | ||
return 'Basic' | ||
case 'boolean': | ||
return 'Basic' | ||
case 'string': | ||
return `Basic realm="${realm}"` | ||
} | ||
} | ||
module.exports = fp(basicPlugin, { | ||
@@ -72,0 +79,0 @@ fastify: '3.x', |
{ | ||
"name": "fastify-basic-auth", | ||
"version": "1.0.1", | ||
"version": "1.1.0", | ||
"description": "Fastify basic auth plugin", | ||
@@ -28,11 +28,12 @@ "main": "index.js", | ||
"devDependencies": { | ||
"fastify": "^3.0.0-rc.4", | ||
"@types/node": "^15.0.0", | ||
"fastify": "^3.0.0", | ||
"fastify-auth": "^1.0.0", | ||
"standard": "^14.3.3", | ||
"tap": "^14.10.7", | ||
"tsd": "^0.11.0" | ||
"standard": "^16.0.1", | ||
"tap": "^15.0.2", | ||
"tsd": "^0.14.0" | ||
}, | ||
"dependencies": { | ||
"basic-auth": "^2.0.1", | ||
"fastify-plugin": "^2.0.0", | ||
"fastify-plugin": "^3.0.0", | ||
"http-errors": "^1.7.3" | ||
@@ -39,0 +40,0 @@ }, |
# fastify-basic-auth | ||
[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](http://standardjs.com/) ![Node CI](https://github.com/fastify/fastify-basic-auth/workflows/Node%20CI/badge.svg) | ||
![CI](https://github.com/fastify/fastify-basic-auth/workflows/CI/badge.svg?branch=master) | ||
[![NPM version](https://img.shields.io/npm/v/fastify-basic-auth.svg?style=flat)](https://www.npmjs.com/package/fastify-basic-auth) | ||
[![Known Vulnerabilities](https://snyk.io/test/github/fastify/fastify-basic-auth/badge.svg)](https://snyk.io/test/github/fastify/fastify-basic-auth) | ||
[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](https://standardjs.com/) | ||
@@ -55,3 +58,3 @@ A simple basic auth plugin for Fastify. | ||
if (username !== 'Tyrion' || password !== 'wine') { | ||
return new Error('Winter is coming') | ||
throw new Error('Winter is coming') | ||
} | ||
@@ -80,3 +83,3 @@ } | ||
if (username !== 'Tyrion' || password !== 'wine') { | ||
return new Error('Winter is coming') | ||
throw new Error('Winter is coming') | ||
} | ||
@@ -134,4 +137,30 @@ } | ||
See code above for examples. | ||
It is also possible to override set the `realm` dynamically by returning it | ||
as the first argument. | ||
```js | ||
const fastify = require('fastify')() | ||
const authenticate = {realm: 'Westeros'} | ||
fastify.register(require('fastify-basic-auth'), { validate, authenticate }) | ||
async function validate (username, password, req, reply) { | ||
if (username !== 'Tyrion' || password !== 'Wine') { | ||
throw new Error('Winter is coming') | ||
} | ||
// custom realm | ||
return 'Lannister' | ||
} | ||
fastify.after(() => { | ||
fastify.route({ | ||
method: 'GET', | ||
url: '/', | ||
onRequest: fastify.basicAuth, | ||
handler: async (req, reply) => { | ||
return { hello: 'world' } | ||
} | ||
}) | ||
}) | ||
``` | ||
### `authenticate` <Boolean|Object> (optional, default: false) | ||
@@ -169,5 +198,4 @@ | ||
## License | ||
Licensed under [MIT](./LICENSE). |
250
test.js
@@ -42,3 +42,3 @@ 'use strict' | ||
t.error(err) | ||
t.strictEqual(res.statusCode, 200) | ||
t.equal(res.statusCode, 200) | ||
}) | ||
@@ -80,4 +80,4 @@ }) | ||
t.error(err) | ||
t.strictEqual(res.statusCode, 401) | ||
t.deepEqual(JSON.parse(res.payload), { | ||
t.equal(res.statusCode, 401) | ||
t.same(JSON.parse(res.payload), { | ||
error: 'Unauthorized', | ||
@@ -123,3 +123,3 @@ message: 'Winter is coming', | ||
t.error(err) | ||
t.strictEqual(res.statusCode, 200) | ||
t.equal(res.statusCode, 200) | ||
}) | ||
@@ -161,4 +161,4 @@ }) | ||
t.error(err) | ||
t.strictEqual(res.statusCode, 401) | ||
t.deepEqual(JSON.parse(res.payload), { | ||
t.equal(res.statusCode, 401) | ||
t.same(JSON.parse(res.payload), { | ||
error: 'Unauthorized', | ||
@@ -204,5 +204,5 @@ message: 'Winter is coming', | ||
}, (err, res) => { | ||
t.is(res.headers['www-authenticate'], 'Basic') | ||
t.equal(res.headers['www-authenticate'], 'Basic') | ||
t.error(err) | ||
t.strictEqual(res.statusCode, 200) | ||
t.equal(res.statusCode, 200) | ||
}) | ||
@@ -244,5 +244,5 @@ }) | ||
}, (err, res) => { | ||
t.is(res.headers['www-authenticate'], 'Basic realm="example"') | ||
t.error(err) | ||
t.strictEqual(res.statusCode, 200) | ||
t.equal(res.headers['www-authenticate'], 'Basic realm="example"') | ||
t.equal(res.statusCode, 200) | ||
}) | ||
@@ -258,3 +258,3 @@ }) | ||
fastify.ready(err => { | ||
t.is(err.message, 'Basic Auth: Missing validate function') | ||
t.equal(err.message, 'Basic Auth: Missing validate function') | ||
}) | ||
@@ -297,4 +297,4 @@ }) | ||
t.error(err) | ||
t.strictEqual(res.statusCode, 401) | ||
t.deepEqual(JSON.parse(res.payload), { | ||
t.equal(res.statusCode, 401) | ||
t.same(JSON.parse(res.payload), { | ||
error: 'Unauthorized', | ||
@@ -342,4 +342,4 @@ message: 'Winter is coming', | ||
t.error(err) | ||
t.strictEqual(res.statusCode, 401) | ||
t.deepEqual(JSON.parse(res.payload), { | ||
t.equal(res.statusCode, 401) | ||
t.same(JSON.parse(res.payload), { | ||
error: 'Unauthorized', | ||
@@ -387,4 +387,4 @@ message: 'Winter is coming', | ||
t.error(err) | ||
t.strictEqual(res.statusCode, 401) | ||
t.deepEqual(JSON.parse(res.payload), { | ||
t.equal(res.statusCode, 401) | ||
t.same(JSON.parse(res.payload), { | ||
error: 'Unauthorized', | ||
@@ -427,4 +427,4 @@ message: 'Winter is coming', | ||
t.error(err) | ||
t.strictEqual(res.statusCode, 401) | ||
t.deepEqual(JSON.parse(res.payload), { | ||
t.equal(res.statusCode, 401) | ||
t.same(JSON.parse(res.payload), { | ||
statusCode: 401, | ||
@@ -472,3 +472,3 @@ error: 'Unauthorized', | ||
t.error(err) | ||
t.strictEqual(res.statusCode, 200) | ||
t.equal(res.statusCode, 200) | ||
}) | ||
@@ -501,3 +501,3 @@ }) | ||
fastify.setErrorHandler(function (err, req, reply) { | ||
t.strictEqual(err.statusCode, 401) | ||
t.equal(err.statusCode, 401) | ||
reply.send(err) | ||
@@ -514,4 +514,4 @@ }) | ||
t.error(err) | ||
t.strictEqual(res.statusCode, 401) | ||
t.deepEqual(JSON.parse(res.payload), { | ||
t.equal(res.statusCode, 401) | ||
t.same(JSON.parse(res.payload), { | ||
error: 'Unauthorized', | ||
@@ -559,4 +559,4 @@ message: 'Winter is coming', | ||
t.error(err) | ||
t.strictEqual(res.statusCode, 401) | ||
t.deepEqual(JSON.parse(res.payload), { | ||
t.equal(res.statusCode, 401) | ||
t.same(JSON.parse(res.payload), { | ||
statusCode: 401, | ||
@@ -569,4 +569,204 @@ error: 'Unauthorized', | ||
test('Invalid options (authenticate)', t => { | ||
t.plan(1) | ||
const fastify = Fastify() | ||
fastify | ||
.register(basicAuth, { validate, authenticate: 'i am invalid' }) | ||
function validate (username, password, req, res, done) { | ||
if (username === 'user' && password === 'pwd') { | ||
done() | ||
} else { | ||
done(new Error('Unauthorized')) | ||
} | ||
} | ||
fastify.ready(function (err) { | ||
t.equal(err.message, 'Basic Auth: Invalid authenticate option') | ||
}) | ||
}) | ||
test('Invalid options (realm is a number)', t => { | ||
t.plan(1) | ||
const fastify = Fastify() | ||
fastify | ||
.register(basicAuth, { validate, authenticate: { realm: 42 } }) | ||
function validate (username, password, req, res, done) { | ||
if (username === 'user' && password === 'pwd') { | ||
done() | ||
} else { | ||
done(new Error('Unauthorized')) | ||
} | ||
} | ||
fastify.ready(function (err) { | ||
t.equal(err.message, 'Basic Auth: Invalid authenticate option') | ||
}) | ||
}) | ||
test('Invalid options (authenticate realm)', t => { | ||
t.plan(3) | ||
const fastify = Fastify() | ||
fastify | ||
.register(basicAuth, { validate, authenticate: { realm: true } }) | ||
function validate (username, password, req, res, done) { | ||
if (username === 'user' && password === 'pwd') { | ||
done() | ||
} else { | ||
done(new Error('Unauthorized')) | ||
} | ||
} | ||
fastify.after(() => { | ||
fastify.route({ | ||
method: 'GET', | ||
url: '/', | ||
preHandler: fastify.basicAuth, | ||
handler: (req, reply) => { | ||
reply.send({ hello: 'world' }) | ||
} | ||
}) | ||
}) | ||
fastify.inject({ | ||
url: '/', | ||
method: 'GET', | ||
headers: { | ||
authorization: basicAuthHeader('user', 'pwd') | ||
} | ||
}, (err, res) => { | ||
t.error(err) | ||
t.equal(res.headers['www-authenticate'], 'Basic') | ||
t.equal(res.statusCode, 200) | ||
}) | ||
}) | ||
test('Invalid options (authenticate realm = undefined)', t => { | ||
t.plan(3) | ||
const fastify = Fastify() | ||
fastify | ||
.register(basicAuth, { validate, authenticate: { realm: undefined } }) | ||
function validate (username, password, req, res, done) { | ||
if (username === 'user' && password === 'pwd') { | ||
done() | ||
} else { | ||
done(new Error('Unauthorized')) | ||
} | ||
} | ||
fastify.after(() => { | ||
fastify.route({ | ||
method: 'GET', | ||
url: '/', | ||
preHandler: fastify.basicAuth, | ||
handler: (req, reply) => { | ||
reply.send({ hello: 'world' }) | ||
} | ||
}) | ||
}) | ||
fastify.inject({ | ||
url: '/', | ||
method: 'GET', | ||
headers: { | ||
authorization: basicAuthHeader('user', 'pwd') | ||
} | ||
}, (err, res) => { | ||
t.error(err) | ||
t.equal(res.headers['www-authenticate'], 'Basic') | ||
t.equal(res.statusCode, 200) | ||
}) | ||
}) | ||
test('WWW-Authenticate Realm dynamic realm', t => { | ||
t.plan(3) | ||
const fastify = Fastify() | ||
const authenticate = { | ||
realm: true | ||
} | ||
fastify.register(basicAuth, { validate, authenticate }) | ||
function validate (username, password, req, res, done) { | ||
if (username === 'user' && password === 'pwd') { | ||
done(null, 'root') | ||
} else { | ||
done(new Error('Unauthorized')) | ||
} | ||
} | ||
fastify.after(() => { | ||
fastify.route({ | ||
method: 'GET', | ||
url: '/', | ||
preHandler: fastify.basicAuth, | ||
handler: (req, reply) => { | ||
reply.send({ hello: 'world' }) | ||
} | ||
}) | ||
}) | ||
fastify.inject({ | ||
url: '/', | ||
method: 'GET', | ||
headers: { | ||
authorization: basicAuthHeader('user', 'pwd') | ||
} | ||
}, (err, res) => { | ||
t.error(err) | ||
t.equal(res.headers['www-authenticate'], 'Basic realm="root"') | ||
t.equal(res.statusCode, 200) | ||
}) | ||
}) | ||
test('WWW-Authenticate Realm dynamic realm promise', t => { | ||
t.plan(3) | ||
const fastify = Fastify() | ||
const authenticate = { | ||
realm: true | ||
} | ||
fastify.register(basicAuth, { validate, authenticate }) | ||
function validate (username, password, req, res) { | ||
if (username === 'user' && password === 'pwd') { | ||
return Promise.resolve('root') | ||
} else { | ||
return Promise.reject(new Error('Unauthorized')) | ||
} | ||
} | ||
fastify.after(() => { | ||
fastify.route({ | ||
method: 'GET', | ||
url: '/', | ||
preHandler: fastify.basicAuth, | ||
handler: (req, reply) => { | ||
reply.send({ hello: 'world' }) | ||
} | ||
}) | ||
}) | ||
fastify.inject({ | ||
url: '/', | ||
method: 'GET', | ||
headers: { | ||
authorization: basicAuthHeader('user', 'pwd') | ||
} | ||
}, (err, res) => { | ||
t.error(err) | ||
t.equal(res.headers['www-authenticate'], 'Basic realm="root"') | ||
t.equal(res.statusCode, 200) | ||
}) | ||
}) | ||
function basicAuthHeader (username, password) { | ||
return 'Basic ' + Buffer.from(`${username}:${password}`).toString('base64') | ||
} |
29768
10
783
198
6
+ Addedfastify-plugin@3.0.1(transitive)
- Removedfastify-plugin@2.3.4(transitive)
- Removedsemver@7.7.1(transitive)
Updatedfastify-plugin@^3.0.0