Last active
November 19, 2016 17:07
-
-
Save gordysc/cc158f33ca41329a720e7777aefa5604 to your computer and use it in GitHub Desktop.
Verifying signed URL's w/ Hapi
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// URL signature has following format: | |
// http://<uri>?timestamp=<timestamp>&signature=<signature> | |
const Boom = require('boom') | |
const crypto = require('crypto') | |
const algorithm = process.env.SIGNATURE_ALGORITHM || 'sha1' | |
const TIMESTAMP_RANGE = parseInt(process.env.TIMESTAMP_RANGE) || 5 * 60 * 1000 | |
const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i | |
const internals = { | |
getApplicationSecret (id) { | |
return (id === '43D96179-8E71-47CC-9755-363AB555EE55') ? 'secret' : false | |
} | |
} | |
exports.register = (server, options, next) => { | |
server.ext('onPreHandler', (request, reply) => { | |
// Verify the request has the required parameters | |
const { signature, timestamp } = request.query | |
if (!signature || !timestamp) { | |
console.log('A signature or a timestamp was not given') | |
return reply(Boom.unauthorized()) | |
} | |
// Verify timestamp is within a specific range of time (5 minutes unless configured) | |
const diff = Math.abs(new Date().getTime() - timestamp) | |
if (!diff || (diff > TIMESTAMP_RANGE)) { | |
console.log('The provided timestamp is outside an acceptable time range for a request') | |
return reply(Boom.unauthorized()) | |
} | |
// Verify the signature has the appropriate format | |
const args = signature.split(':') | |
if (args.length !== 2) { | |
console.log('The request signature does not have the appropriate format') | |
return reply(Boom.unauthorized()) | |
} | |
// Verify the 1st part of the signature is a valid v4 UUID | |
const id = args[0] | |
if (!UUID_REGEX.test(id)) { | |
console.log('The application ID is not a valid v4 UUID') | |
return reply(Boom.unauthorized()) | |
} | |
// Get the application secret (if it exists) | |
const secret = internals.getApplicationSecret(id) | |
if (!secret) { | |
console.log('This application does not exist') | |
return reply(Boom.unauthorized()) | |
} | |
// Calculate the expected hash | |
const { path } = request | |
const hmac = crypto.createHmac(algorithm, secret) | |
const expected = hmac.update(`${path}?timestamp=${timestamp}`).digest('hex') | |
// Verify the 2nd part of the signature matches the expected generated hash | |
const hash = args[1] | |
if (hash !== expected) { | |
console.log('The provided hash did not match the expected result') | |
return reply(Boom.unauthorized()) | |
} | |
// We have a successful signature, continue | |
return reply.continue() | |
}) | |
return next() | |
} | |
exports.register.attributes = { | |
name: 'pre-handler-hook', | |
version: process.env.npm_package_version, | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment