Skip to content
This repository has been archived by the owner on Apr 20, 2019. It is now read-only.

Commit

Permalink
Merge pull request #35 from hapijs/v17
Browse files Browse the repository at this point in the history
Update to hapi v17.x.x
  • Loading branch information
mtharrison authored Feb 20, 2018
2 parents e630836 + 6fe7b5f commit 0b74dbe
Show file tree
Hide file tree
Showing 8 changed files with 693 additions and 775 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ lib-cov
*.swp
*.swo
*.swn

package-lock.json
5 changes: 3 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
language: node_js

node_js:
- 0.10
- 4
- "8"
- "9"
- "node"

sudo: false
3 changes: 2 additions & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
Copyright (c) 2012-2014, Walmart and other contributors.
Copyright (c) 2012-2017, Project contributors.
Copyright (c) 2012-2014, Walmart.
All rights reserved.

Redistribution and use in source and binary forms, with or without
Expand Down
113 changes: 81 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,35 +11,59 @@ Lead Maintainer: [Danny Coates](https://github.com/dannycoates)
[Hawk authentication](https://github.com/hueniverse/hawk) provides a holder-of-key authentication scheme. The scheme supports payload
authentication. The scheme requires the following options:

- `getCredentialsFunc` - credential lookup function with the signature `function(id, callback)` where:
- `getCredentialsFunc` - credential lookup function with the signature `[async] function(id)` where:
- `id` - the Hawk credentials identifier.
- `callback` - the callback function with signature `function(err, credentials)` where:
- `err` - an internal error.
- `credentials` - a credentials object passed back to the application in `request.auth.credentials`. Return `null` or `undefined` to
- _throws_ an internal error.
- _returns_ `{ credentials }` object where:
- `credentials` a credentials object passed back to the application in `request.auth.credentials`. Set to be `null` or `undefined` to
indicate unknown credentials (which is not considered an error state).
- `hawk` - optional protocol options passed to `Hawk.server.authenticate()`.

```javascript
var Hapi = require('hapi');
var HapiHawk = require('hapi-auth-hawk');
var server = new Hapi.Server();
server.connection();
const Hapi = require('hapi');

var credentials = {
const credentials = {
d74s3nz2873n: {
key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
algorithm: 'sha256'
}
}
};

var getCredentials = function (id, callback) {
const getCredentialsFunc = function (id) {

return callback(null, credentials[id]);
return credentials[id];
};

server.register(HapiHawk, function (err) {
const start = async () => {

const server = Hapi.server({ port: 4000 });

await server.register(require('hapi-auth-hawk'));

server.auth.strategy('default', 'hawk', { getCredentialsFunc });
server.auth.default('default');

server.route({
method: 'GET',
path: '/',
handler: function (request, h) {

return 'welcome';
}
});

await server.start();

server.auth.strategy('default', 'hawk', { getCredentialsFunc: getCredentials });
console.log('Server started listening on %s', server.info.uri);
};

start();

// Ensure process exits on unhandled rejection

process.on('unhandledRejection', (err) => {

throw err;
});

```
Expand All @@ -50,49 +74,74 @@ server.register(HapiHawk, function (err) {
including a token (bewit) in the request query, issued by an authorized party. Bewit is a subset of the Hawk protocol. The scheme can only
be used with 'GET' requests and requires the following options:

- `getCredentialsFunc` - credential lookup function with the signature `function(id, callback)` where:
- `getCredentialsFunc` - credential lookup function with the signature `async function(id)` where:
- `id` - the Hawk credentials identifier.
- `callback` - the callback function with signature `function(err, credentials)` where:
- `err` - an internal error.
- `credentials` - a credentials object passed back to the application in `request.auth.credentials`. Return `null` or `undefined` to
indicate unknown credentials (which is not considered an error state).
- _throws_ an internal error.
- _returns_ `{ credentials }` object where:
- `credentials` a credentials object passed back to the application in `request.auth.credentials`. Set to be `null` or `undefined` to
indicate unknown credentials (which is not considered an error state).
- `hawk` - optional protocol options passed to `Hawk.server.authenticateBewit()`.

```javascript
var Hapi = require('hapi');
var server = new Hapi.Server();
server.connection();
const Hapi = require('hapi');

var credentials = {
const credentials = {
d74s3nz2873n: {
key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
algorithm: 'sha256'
}
}
};

const getCredentialsFunc = function (id) {

return credentials[id];
};

const start = async () => {

const server = Hapi.server({ port: 4000 });

var getCredentials = function (id, callback) {
await server.register(require('.'));

return callback(null, credentials[id]);
server.auth.strategy('default', 'bewit', { getCredentialsFunc });
server.auth.default('default');

server.route({
method: 'GET',
path: '/',
handler: function (request, h) {

return 'welcome';
}
});

await server.start();

console.log('Server started listening on %s', server.info.uri);
};

server.register(require('hapi-auth-hawk'), function (err) {
start();

// Ensure process exits on unhandled rejection

process.on('unhandledRejection', (err) => {

server.auth.strategy('default', 'bewit', { getCredentialsFunc: getCredentials });
throw err;
});
```

To send an authenticated Bewit request, the URI must contain the `'bewit'` query parameter which can be generated using the Hawk module:

```javascript
var Hawk = require('hawk');
const Hawk = require('hawk');

var credentials = {
const credentials = {
id: 'd74s3nz2873n',
key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
algorithm: 'sha256'
};

var uri = 'http://example.com:8080/endpoint';
var bewit = Hawk.client.getBewit(uri, { credentials: credentials, ttlSec: 60 });
let uri = 'http://example.com:8080/endpoint';
const bewit = Hawk.client.getBewit(uri, { credentials: credentials, ttlSec: 60 });
uri += '?bewit=' + bewit;
```
124 changes: 61 additions & 63 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
'use strict';

// Load modules

var Boom = require('boom');
var Hoek = require('hoek');
var Hawk = require('hawk');
const Boom = require('boom');
const Hawk = require('hawk');
const Hoek = require('hoek');


// Declare internals

var internals = {};
const internals = {};


exports.register = function (server, options, next) {
exports.plugin = {
pkg: require('../package.json'),
register: function (server) {

server.auth.scheme('hawk', internals.hawk);
server.auth.scheme('bewit', internals.bewit);
return next();
};


exports.register.attributes = {
pkg: require('../package.json')
server.auth.scheme('hawk', internals.hawk);
server.auth.scheme('bewit', internals.bewit);
}
};


Expand All @@ -28,78 +27,78 @@ internals.hawk = function (server, options) {
Hoek.assert(options, 'Invalid hawk scheme options');
Hoek.assert(options.getCredentialsFunc, 'Missing required getCredentialsFunc method in hawk scheme configuration');

var settings = Hoek.clone(options);
const settings = Hoek.clone(options);
settings.hawk = settings.hawk || {};

var scheme = {
authenticate: function (request, reply) {

Hawk.server.authenticate(request.raw.req, settings.getCredentialsFunc, settings.hawk, function (err, credentials, artifacts) {
const scheme = {
authenticate: async function (request, h) {

if (!err) {
request.once('peek', function (chunk) {

var payloadHash = Hawk.crypto.initializePayloadHash(request.auth.credentials.algorithm, request.headers['content-type']);
payloadHash.update(chunk);

request.on('peek', function (chunk2) {
try {
var { credentials, artifacts } = await Hawk.server.authenticate(request.raw.req, settings.getCredentialsFunc, settings.hawk);
}
catch (err) {
({ credentials, artifacts } = err);
return h.unauthenticated(err, credentials ? { credentials, artifacts } : undefined);
}

payloadHash.update(chunk2);
});
request.events.once('peek', (chunk) => {

request.once('finish', function () {
const payloadHash = Hawk.crypto.initializePayloadHash(request.auth.credentials.algorithm, request.headers['content-type']);
payloadHash.update(chunk);

request.plugins['hapi-auth-hawk'] = { payloadHash: Hawk.crypto.finalizePayloadHash(payloadHash) };
});
});
}
request.events.on('peek', (chunk2) => payloadHash.update(chunk2));

var result = { credentials: credentials, artifacts: artifacts };
if (err) {
return reply(err, null, result);
}
request.events.once('finish', () => {

return reply.continue(result);
request.plugins['hapi-auth-hawk'] = { payloadHash: Hawk.crypto.finalizePayloadHash(payloadHash) };
});
});

return h.authenticated({ credentials, artifacts });
},
payload: function (request, reply) {
payload: function (request, h) {

if (!request.auth.artifacts.hash) {
return reply(Boom.unauthorized(null, 'Hawk')); // Missing
throw Boom.unauthorized(null, 'Hawk'); // Missing
}

var plugin = request.plugins['hapi-auth-hawk'];
if (plugin &&
Hawk.server.authenticatePayloadHash(plugin.payloadHash, request.auth.artifacts)) {
const plugin = request.plugins['hapi-auth-hawk'];

return reply.continue();
if (!plugin) {
throw Boom.unauthorized('Payload is invalid');
}

return reply(Boom.unauthorized('Payload is invalid'));
try {
Hawk.server.authenticatePayloadHash(plugin.payloadHash, request.auth.artifacts);
return h.continue;
}
catch (err) {
throw Boom.unauthorized('Payload is invalid');
}
},
response: function (request, reply) {
response: function (request, h) {

var response = request.response;
var payloadHash = Hawk.crypto.initializePayloadHash(request.auth.credentials.algorithm, response.headers['content-type']);
const response = request.response;
const payloadHash = Hawk.crypto.initializePayloadHash(request.auth.credentials.algorithm, response.headers['content-type']);

response.header('trailer', 'server-authorization');
response.header('transfer-encoding', 'chunked');
// We must not send a content-length header alongside transfer-encoding.
// see https://tools.ietf.org/html/rfc7230#section-3.3.3
delete response.headers['content-length'];

response.on('peek', function (chunk) {
response.events.on('peek', (chunk) => {

payloadHash.update(chunk);
});

response.once('finish', function () {
response.events.once('finish', () => {

var header = Hawk.server.header(request.auth.credentials, request.auth.artifacts, { hash: Hawk.crypto.finalizePayloadHash(payloadHash) });
const header = Hawk.server.header(request.auth.credentials, request.auth.artifacts, { hash: Hawk.crypto.finalizePayloadHash(payloadHash) });
request.raw.res.addTrailers({ 'server-authorization': header });
});

return reply.continue();
return h.continue;
}
};

Expand All @@ -112,21 +111,20 @@ internals.bewit = function (server, options) {
Hoek.assert(options, 'Invalid bewit scheme options');
Hoek.assert(options.getCredentialsFunc, 'Missing required getCredentialsFunc method in bewit scheme configuration');

var settings = Hoek.clone(options);
const settings = Hoek.clone(options);
settings.hawk = settings.hawk || {};

var scheme = {
authenticate: function (request, reply) {

Hawk.server.authenticateBewit(request.raw.req, settings.getCredentialsFunc, settings.hawk, function (err, credentials, bewit) {
const scheme = {
authenticate: async function (request, h) {

var result = { credentials: credentials, artifacts: bewit };
if (err) {
return reply(err, null, result);
}

return reply.continue(result);
});
try {
const { credentials, attributes } = await Hawk.server.authenticateBewit(request.raw.req, settings.getCredentialsFunc, settings.hawk);
return h.authenticated({ credentials, attributes });
}
catch (err) {
const { credentials, attributes } = err;
return h.unauthenticated(err, credentials ? { credentials, attributes } : undefined);
}
}
};

Expand Down
Loading

0 comments on commit 0b74dbe

Please sign in to comment.