diff --git a/Makefile b/Makefile index a2cd883..e9c9544 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ test: - @node node_modules/lab/bin/lab -test-cov: - @node node_modules/lab/bin/lab -t 100 + @node node_modules/lab/bin/lab -a code -L +test-cov: + @node node_modules/lab/bin/lab -a code -t 100 -L test-cov-html: - @node node_modules/lab/bin/lab -r html -o coverage.html + @node node_modules/lab/bin/lab -a code -r html -o coverage.html .PHONY: test test-cov test-cov-html diff --git a/README.md b/README.md index 6853d34..9df6009 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ ![scarecrow Logo](https://raw.github.com/hueniverse/scarecrow/master/images/scarecrow.png) -Oz authorization plugin for [**hapi**](https://github.com/spumko/hapi) +Oz authorization plugin for [**hapi**](https://github.com/hapijs/hapi) [![Build Status](https://secure.travis-ci.org/hueniverse/scarecrow.png)](http://travis-ci.org/hueniverse/scarecrow) - diff --git a/index.js b/index.js index 4cc88b3..bb0a047 100755 --- a/index.js +++ b/index.js @@ -1 +1 @@ -module.exports = require('./lib'); \ No newline at end of file +module.exports = require('./lib'); diff --git a/lib/index.js b/lib/index.js index 89d4836..80ccb5e 100755 --- a/lib/index.js +++ b/lib/index.js @@ -1,7 +1,7 @@ // Load modules -var Hoek = require('hoek'); var Boom = require('boom'); +var Hoek = require('hoek'); var Oz = require('oz'); @@ -34,9 +34,9 @@ internals.defaults = { }; -exports.register = function (plugin, options, next) { +exports.register = function (server, options, next) { - plugin.auth.scheme('oz', internals.oz); + server.auth.scheme('oz', internals.oz); next(); }; @@ -82,7 +82,11 @@ internals.oz = function (server, options) { Oz.server.authenticate(request.raw.req, settings.oz.encryptionPassword, {}, function (err, credentials, artifacts) { - return reply(err, { credentials: credentials, artifacts: artifacts }); + if (err) { + return reply(Boom.unauthorized(err, null, { credentials: credentials })); + } + + return reply.continue({ credentials: credentials, artifacts: artifacts }); }); } }; diff --git a/package.json b/package.json index 89f8ddd..74b38dc 100755 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "authorization" ], "engines": { - "node": ">=0.10.22" + "node": ">=0.10.32" }, "dependencies": { "hoek": "2.x.x", @@ -23,11 +23,12 @@ "oz": "0.4.x" }, "peerDependencies": { - "hapi": ">=2.x.x" + "hapi": ">=8.x.x" }, "devDependencies": { - "hapi": "6.x.x", - "lab": "3.x.x" + "code": "1.x.x", + "hapi": "8.x.x", + "lab": "5.x.x" }, "scripts": { "test": "make test-cov" diff --git a/test/index.js b/test/index.js index 2781eda..631aee8 100755 --- a/test/index.js +++ b/test/index.js @@ -1,7 +1,8 @@ // Load modules -var Lab = require('lab'); +var Code = require('code'); var Hapi = require('hapi'); +var Lab = require('lab'); var Oz = require('oz'); @@ -12,11 +13,10 @@ var internals = {}; // Test shortcuts -var expect = Lab.expect; -var before = Lab.before; -var after = Lab.after; -var describe = Lab.experiment; -var it = Lab.test; +var lab = exports.lab = Lab.script(); +var describe = lab.describe; +var it = lab.it; +var expect = Code.expect; describe('Scarecrow', function () { @@ -69,9 +69,11 @@ describe('Scarecrow', function () { }; var server = new Hapi.Server(); - server.pack.register(require('../'), function (err) { + server.connection(); + + server.register(require('../'), function (err) { - expect(err).to.not.exist; + expect(err).to.not.exist(); // Add strategy @@ -99,7 +101,7 @@ describe('Scarecrow', function () { Oz.ticket.rsvp(apps.social, grant, encryptionPassword, {}, function (err, rsvp) { - expect(err).to.not.exist; + expect(err).to.not.exist(); // After granting app access, the user returns to the app with the rsvp // The app exchanges the rsvp for a ticket @@ -150,6 +152,110 @@ describe('Scarecrow', function () { }); }); }); -}); + it('fails to authenticate a request with mismatching app id', function (done) { + + var encryptionPassword = 'password'; + + var app = { + id: 'social', + scope: ['a', 'b', 'c'], + key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn', + algorithm: 'sha256' + }; + + var grant = { + id: 'a1b2c3d4e5f6g7h8i9j0', + app: 'social', + user: 'john', + exp: Oz.hawk.utils.now() + 60000 + }; + + var options = { + oz: { + encryptionPassword: encryptionPassword, + + loadAppFunc: function (id, callback) { + + callback(null, app); + }, + + loadGrantFunc: function (id, callback) { + + callback(null, grant); + } + } + }; + + var server = new Hapi.Server(); + server.connection(); + + server.register(require('../'), function (err) { + + expect(err).to.not.exist(); + + // Add strategy + + server.auth.strategy('oz', 'oz', true, options); + + // Add a protected resource + + server.route({ path: '/protected', method: 'GET', config: { auth: { entity: 'user' }, handler: function (request, reply) { reply(request.auth.credentials.user + ' your in!'); } } }); + + // The app requests an app ticket using Hawk authentication + + var req = { + method: 'POST', + url: 'http://example.com/oz/app', + headers: { + authorization: Oz.client.header('http://example.com/oz/app', 'POST', app).field + } + }; + + server.inject(req, function (res) { + + // The user is redirected to the server, logs in, and grant app access, resulting in an rsvp + + var appTicket = res.result; + + Oz.ticket.rsvp(app, grant, encryptionPassword, {}, function (err, rsvp) { + + expect(err).to.not.exist(); + + // After granting app access, the user returns to the app with the rsvp + // The app exchanges the rsvp for a ticket + + var req = { + method: 'POST', + url: 'http://example.com/oz/rsvp', + headers: { + authorization: Oz.client.header('http://example.com/oz/rsvp', 'POST', appTicket).field + }, + payload: JSON.stringify({ rsvp: rsvp }) + }; + + server.inject(req, function (res) { + + var userTicket = res.result; + userTicket.app = '567'; + + var req = { + method: 'GET', + url: 'http://example.com/protected', + headers: { + authorization: Oz.client.header('http://example.com/protected', 'GET', userTicket).field + } + }; + + server.inject(req, function (res) { + expect(res.statusCode).to.equal(401); + expect(res.result.message).to.equal('Error: Mismatching application id'); + done(); + }); + }); + }); + }); + }); + }); +});