NOTE: Use v1.0.1 from npm for Hapi version 7.x.x and v2.x.x for Hapi version 8.x.x
NOTE: In version 0.11.0 of http-signature the verify method has been split into verifyHMAC and verifySignature. The example below uses an HMAC scheme, and so the verify function is using verifyHMAC.
First, a big hat tip to the guys that created the hapi framework.
This is a signature authentication scheme for hapi that wraps the Joyent Signature Authentication Scheme and requires a validating signed authorization header. (Note: the format of this plugin, including the docs here, follows the hapi-auth-basic plugin).
The hapi-auth-signature scheme takes the following options.
-
validateFunc- (required) validation function with the signaturefunction(request, parsedSignature, callback)where:request- hapi request object.parsedSignature- http-signature parsed signature from header.callback- a callback function with the signaturefunction(err, isValid, credentials)where:err- an internal error.isValid-trueif the signature is verified, otherwisefalse.credentials- a credentials object passed back to the application inrequest.auth.credentials. Typically,credentialsare only included whenisValidistrue, but there are cases when the application needs to know who tried to authenticate even when it fails (e.g. with authentication mode'try').
-
payloadFunc- (optional) payload validation function with the signaturefunction(request, callback)where:request- hapi request object.callback- a callback function with the signaturefunction(err, isValid)where:err- an internal error.isValid-trueif the payload is verified, otherwisefalse.
The validation function shown below is based on an hmac strategy with a key identifier and secret key stored in a user record. http-signature supports the following algorithms:
- rsa-sha1
- rsa-sha256
- rsa-sha512
- dsa-sha1
- hmac-sha1
- hmac-sha256
- hmac-sha512
Public key algorithms will almost certainly require their own validating function (and therefore strategy). The algorithm used can be retrieved from the header as it's part of the http-signature scheme. See the docs at http-signature for more details.
var HttpSignature = require('http-signature');
var users = [
{
id: 1,
username: 'john',
apikeyid: '18KF2FGK6807ZQA945R2',
secretkey: '46573e78ce9df4f2d9ae93afd5f5c281'
}
];
var validate = function (request, parsedSignature, callback) {
var keyId = parsedSignature.keyId;
var credentials = {};
var secretKey;
users.forEach(function(user, index) {
if (user.apikeyid === keyId) {
secretKey = user.secretkey;
credentials = {id: user.id, username: user.username};
}
});
if (!secretKey) {
return callback(null, false);
}
if(HttpSignature.verifyHMAC(parsedSignature, secretKey)) {
callback(null, true, credentials);
} else {
callback(null, false);
};
};
server.pack.register(require('hapi-auth-signature'), function (err) {
server.auth.strategy('hmac', 'signature', { validateFunc: validate });
server.route({ method: 'GET', path: '/', config: { auth: 'hmac' } });
});