hapi-auth-jwt2
Advanced tools
Comparing version 1.0.4 to 1.0.5
var Hapi = require('hapi'); | ||
var hapiAuthJWT = require('../lib/'); | ||
var jwt = require('jsonwebtoken'); | ||
var port = process.env.PORT || 8000; | ||
var JWT = require('jsonwebtoken'); // used to sign our content | ||
var port = process.env.PORT || 8000; // allow port to be set | ||
var secret = 'NeverShareThisYourSecret'; // Never Share This! even in private GitHub repos! | ||
var secret = 'NeverShareYourSecret'; // Never Share This! even in private GitHub repos! | ||
@@ -16,15 +16,16 @@ var people = { | ||
// use the token as the 'authorization' header in requests | ||
var token = jwt.sign(people[1], secret); | ||
// defining our own validate function lets us do something | ||
// useful/custom with the decodedToken before reply(ing) | ||
var token = JWT.sign(people[1], secret); // synchronous | ||
console.log(token); | ||
// bring your own validation function | ||
var validate = function (decoded, callback) { | ||
console.log(decoded); // | ||
console.log(decoded); | ||
if (!people[decoded.id]) { // invalid person | ||
// do your checks to see if the person is valid | ||
if (!people[decoded.id]) { | ||
return callback(null, false); | ||
} | ||
return callback(null, true); | ||
else { | ||
return callback(null, true); | ||
} | ||
}; | ||
@@ -40,38 +41,21 @@ | ||
} | ||
// see: http://hapijs.com/api#serverauthschemename-scheme | ||
server.auth.strategy('jwt', 'jwt', true, { key: secret, validateFunc: validate }); | ||
server.route({ | ||
// GET http://localhost:8000/private (Auth Token Required) | ||
// curl -H "Authorization: <TOKEN>" http://localhost:8000/private | ||
/** example: | ||
curl -v -H "Authorization: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwibmFtZSI6IkFudGhvbnkgVmFsaWQgVXNlciIsImlhdCI6MTQyNTQxMTA0Nn0.HUlnTh_LMQSJmfBB3OVqThypm0nnyQjm5jbrAKDgOhI" \ | ||
http://localhost:8000/private | ||
*/ | ||
method: 'GET', | ||
path: '/private', | ||
config: { auth: 'jwt' }, | ||
handler: function(request, reply) { | ||
var replyObj = { | ||
text: 'You used a Token! :-)' | ||
// credentials: request.auth.credentials | ||
}; | ||
reply(replyObj).header("Authorization", request.headers.authorization); | ||
server.route([ | ||
{ | ||
method: "GET", path: "/", config: { auth: false }, | ||
handler: function(request, reply) { | ||
reply({text: 'Token not required'}); | ||
} | ||
}, | ||
{ | ||
method: 'GET', path: '/restricted', config: { auth: 'jwt' }, | ||
handler: function(request, reply) { | ||
reply({text: 'You used a Token!'}).header("Authorization", request.headers.authorization); | ||
} | ||
} | ||
}); | ||
server.route({ | ||
// GET to http://localhost:8000/ (No Auth Token Required) | ||
// This get can be executed without sending any token at all | ||
method: "GET", | ||
path: "/", | ||
config: { auth: false }, | ||
handler: function(request, reply) { | ||
var replyObj = {text: 'Token not required'}; | ||
reply(replyObj); | ||
} | ||
}); | ||
]); | ||
}); | ||
server.start(); |
// Load modules | ||
var Boom = require('boom'); // error handling https://github.com/hapijs/boom | ||
var Hoek = require('hoek'); // hapi utilities https://github.com/hapijs/hoek | ||
var JWT = require('jsonwebtoken'); // https://github.com/docdis/learn-json-web-tokens | ||
var Boom = require('boom'); | ||
var Hoek = require('hoek'); | ||
var JWT = require('jsonwebtoken'); | ||
var internals = {}; // Declare internals >> see: http://hapijs.com/styleguide | ||
// Declare internals >> see: http://hapijs.com/styleguide | ||
var internals = {}; | ||
exports.register = function (server, options, next) { | ||
@@ -16,5 +13,4 @@ server.auth.scheme('jwt', internals.implementation); | ||
// hapi requires attributes for a plugin. See: http://hapijs.com/tutorials/plugins | ||
exports.register.attributes = { | ||
pkg: require('../package.json') | ||
exports.register.attributes = { // hapi requires attributes for a plugin. | ||
pkg: require('../package.json') // See: http://hapijs.com/tutorials/plugins | ||
}; | ||
@@ -24,5 +20,4 @@ | ||
// pre-auth checks | ||
Hoek.assert(options, 'Missing JWT auth options'); | ||
Hoek.assert(options.key, 'options does not contain secret key'); | ||
Hoek.assert(options, 'Missing JWT auth options'); // pre-auth checks | ||
Hoek.assert(options.key, 'options does not contain secret key'); // no signing key | ||
Hoek.assert(typeof options.validateFunc === 'function', 'options.validateFunc MUST be a Valid Function'); | ||
@@ -32,12 +27,9 @@ | ||
authenticate: function (request, reply) { | ||
var auth = request.headers.authorization; | ||
var auth = request.headers.authorization; | ||
// console.log("AUTH: "+auth); | ||
if (!auth) { | ||
return reply(Boom.unauthorized(null, 'Token not present')); | ||
} | ||
else { | ||
// strip pointless "Bearer " label & any whitespace > http://git.io/xP4F | ||
else { // strip pointless "Bearer " label & any whitespace > http://git.io/xP4F | ||
var token = auth.replace(/Bearer/gi,'').replace(/ /g,''); | ||
// rudimentary check for JWT validity see: http://git.io/xPBn for JWT format | ||
@@ -47,4 +39,3 @@ if(token.split('.').length !== 3) { | ||
} | ||
else { | ||
else { // attempt to verify the token | ||
JWT.verify(token, options.key, function(err, decoded) { | ||
@@ -54,12 +45,7 @@ if (err) { | ||
} | ||
else { | ||
// see: http://hapijs.com/tutorials/auth for validateFunc signature | ||
options.validateFunc(decoded, function (err, valid) { | ||
// not much point validating the token at this point... | ||
// if (err || !valid) { | ||
// return reply(Boom.unauthorized('Invalid token', 'Token'), null, { credentials: "Please Login." }); | ||
// } | ||
else { // see: http://hapijs.com/tutorials/auth for validateFunc signature | ||
options.validateFunc(decoded, function (err, valid) { // bring your own checks | ||
// Authenticated | ||
return reply.continue({ credentials: "See Authorization Header!" }); | ||
// the object containing a credentials property is required by Hapi... | ||
// the object containing a credentials property is REQUIRED by Hapi... | ||
}); | ||
@@ -72,4 +58,3 @@ } | ||
}; | ||
return scheme; | ||
}; |
{ | ||
"name": "hapi-auth-jwt2", | ||
"version": "1.0.4", | ||
"version": "1.0.5", | ||
"description": "Hapi.js Authentication Plugin/Scheme using JSON Web Tokens (JWT)", | ||
@@ -40,3 +40,3 @@ "main": "lib/index.js", | ||
"engines": { | ||
"node": ">=0.10.x" | ||
"node": ">=0.10" | ||
}, | ||
@@ -43,0 +43,0 @@ "scripts": { |
163
README.md
# Hapi Auth with JSON Web Tokens (JWT) | ||
A stateless authentication scheme/plugin for | ||
The ***simplest*** authentication scheme/plugin for | ||
[Hapi.js](http://hapijs.com/) apps using JSON Web Tokens. | ||
[](https://github.com/ideaq/hapi-auth-jwt) | ||
[![Node.js Version][node-version-image]][node-version-url] | ||
[![NPM Version][npm-image]][npm-url] | ||
[](https://travis-ci.org/ideaq/hapi-auth-jwt2) | ||
@@ -11,8 +13,153 @@ [](https://codeclimate.com/github/ideaq/hapi-auth-jwt2) | ||
[](https://david-dm.org/ideaq/hapi-auth-jwt2) | ||
[](https://david-dm.org/ideaq/hapi-auth-jwt2#info=devDependencies) | ||
## Background Research | ||
## Usage | ||
Borrowed code from the following: | ||
### Install from NPM | ||
```sh | ||
npm install hapi-auth-jwt2 --save | ||
``` | ||
### Example | ||
There is a an example in the /**example** directory of this repo | ||
and we are preparing a more [real-world](https://github.com/ideaq/hapi-auth-jwt2/issues/9) | ||
example you can drop into your app and go! | ||
But here are the basic usage to get started: | ||
```javascript | ||
var Hapi = require('hapi'); | ||
var JWT = require('jsonwebtoken'); // used to sign our content | ||
var port = process.env.PORT || 8000; // allow port to be set | ||
var secret = 'NeverShareYourSecret'; // Never Share This! even in private GitHub repos! | ||
var people = { // our "users databse" | ||
1: { | ||
id: 1, | ||
name: 'Anthony Valid User' | ||
} | ||
}; | ||
// use the token as the 'authorization' header in requests | ||
var token = JWT.sign(people[1], secret); // synchronous | ||
// bring your own validation function | ||
var validate = function (decoded, callback) { | ||
console.log(decoded); | ||
// do your checks to see if the person is valid | ||
if (!people[decoded.id]) { | ||
return callback(null, false); | ||
} | ||
else { | ||
return callback(null, true); | ||
} | ||
}; | ||
var server = new Hapi.Server(); | ||
server.connection({ port: port }); | ||
// include our module here ↓↓ | ||
server.register(require('hapi-auth-jwt2'), function (err) { | ||
if(err){ | ||
console.log(err); | ||
} | ||
// see: http://hapijs.com/api#serverauthschemename-scheme | ||
server.auth.strategy('jwt', 'jwt', true, { key: secret, validateFunc: validate }); | ||
server.route([ | ||
{ | ||
method: "GET", path: "/", config: { auth: false }, | ||
handler: function(request, reply) { | ||
reply({text: 'Token not required'}); | ||
} | ||
}, | ||
{ | ||
method: 'GET', path: '/restricted', config: { auth: 'jwt' }, | ||
handler: function(request, reply) { | ||
reply({text: 'You used a Token!'}).header("Authorization", request.headers.authorization); | ||
} | ||
} | ||
]); | ||
}); | ||
server.start(); | ||
``` | ||
Run the server with: `node example/server.js` | ||
Now use **curl** to access the two routes: | ||
#### No Token Required | ||
```sh | ||
curl -v http://localhost:8000/ | ||
``` | ||
#### Token Required | ||
Try to access the /*restricted* content *without* supplying a Token | ||
(*expect* to see a ***401 error***): | ||
```sh | ||
curl -v http://localhost:8000/restricted | ||
``` | ||
Now access the url using the following format: | ||
`curl -H "Authorization: <TOKEN>" http://localhost:8000/restricted` | ||
A here's a *valid* token you can use (*copy-paste* this command): | ||
```sh | ||
curl -v -H "Authorization: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwibmFtZSI6IkFudGhvbnkgVmFsaWQgVXNlciIsImlhdCI6MTQyNTQ3MzUzNX0.KA68l60mjiC8EXaC2odnjFwdIDxE__iDu5RwLdN1F2A" \ | ||
http://localhost:8000/restricted | ||
``` | ||
That's it. | ||
Write your own `validateFunc` with what ever checks you want to perform | ||
on the **decoded** token before allowing the visitor to proceed. | ||
- - - | ||
## Motivation | ||
While making [***Time***](https://github.com/ideaq/time) we want to ensure | ||
our app (and API) is as ***simple*** as *possible* to use. | ||
This lead us to using JSON Web Tokens for ***Stateless*** Authentication. | ||
We did a *extensive* [research](https://www.npmjs.com/search?q=hapi+auth+jwt) | ||
into *existing* modules that *might* solve our problem; there are *many* on NPM: | ||
 | ||
but they were invariably ***too complicated***, poorly documented and | ||
had *useless* (non-real-world) "examples"! | ||
So we decided to write our own addressing all these issues. | ||
*Don't take our word for it, do your own homework and decide which module you prefer.* | ||
### *Guiding Principal* | ||
> "* **perfection** is **attained** not when there is nothing more to add, | ||
> but when there is **nothing more to remove*** " ~ | ||
[Antoine de Saint-Exupéry](http://en.wikiquote.org/wiki/Antoine_de_Saint_Exup%C3%A9ry) | ||
## Why hapi-auth-jwt2 ? | ||
The name we wanted was taken. | ||
Think of our module as the "***new and simplified version***" :wink: | ||
## Useful Links | ||
For more background on JWT see our post: | ||
https://github.com/docdis/learn-json-web-tokens | ||
### Hapi.js Auth | ||
We borrowed code from the following: | ||
+ http://hapijs.com/tutorials/auth | ||
@@ -22,2 +169,8 @@ + https://github.com/hapijs/hapi-auth-basic | ||
+ https://github.com/hapijs/hapi-auth-hawk | ||
+ https://github.com/ryanfitz/hapi-auth-jwt | ||
[npm-image]: https://img.shields.io/npm/v/hapi-auth-jwt2.svg?style=flat | ||
[npm-url]: https://npmjs.org/package/hapi-auth-jwt2 | ||
[node-version-image]: https://img.shields.io/node/v/hapi-auth-jwt2.svg?style=flat | ||
[node-version-url]: http://nodejs.org/download/ |
var Hapi = require('hapi'); | ||
var JWT = require('jsonwebtoken'); | ||
var secret = 'NeverShareThisYourSecret'; | ||
var secret = 'NeverShareYourSecret'; | ||
@@ -5,0 +5,0 @@ // for debug options see: http://hapijs.com/tutorials/logging |
var test = require('tape'); | ||
var JWT = require('jsonwebtoken'); | ||
var secret = 'NeverShareThisYourSecret'; | ||
var secret = 'NeverShareYourSecret'; | ||
@@ -61,5 +61,5 @@ var server = require('./server'); // test server which in turn loads our module | ||
test("Access restricted content (with an VALID Token)", function(t) { | ||
test("Access restricted content (with VALID Token)", function(t) { | ||
// use the token as the 'authorization' header in requests | ||
var token = JWT.sign({ id:1,"name":"Old Greg" }, secret); | ||
var token = JWT.sign({ id:1,"name":"Charlie" }, secret); | ||
var options = { | ||
@@ -72,5 +72,6 @@ method: "POST", | ||
server.inject(options, function(response) { | ||
t.equal(response.statusCode, 200, "INVALID Token should fail"); | ||
t.equal(response.statusCode, 200, "VALID Token should succeed!"); | ||
t.end(); | ||
}); | ||
}); |
16093
175
195