├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── examples └── basic │ ├── home.hbs │ └── index.js ├── index.js ├── lib └── index.js ├── package.json └── test ├── index.js └── test.hbs /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Dependency directory 23 | # Commenting this out is preferred by some people, see 24 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 25 | node_modules 26 | 27 | # Users Environment Variables 28 | .lock-wscript 29 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - 0.10 5 | - 0.12 6 | - iojs 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Matt Harrison 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hapi-context-credentials 2 | 3 | [![Build Status](https://travis-ci.org/mtharrison/hapi-context-credentials.svg)](https://travis-ci.org/mtharrison/hapi-context-credentials) 4 | 5 | hapi.js plugin - Include `request.auth.credentials` in default view context, if user is authenticated. 6 | 7 | ##Install 8 | 9 | `npm install --save hapi-context-credentials` 10 | 11 | ##Background 12 | 13 | For pretty much any website that has a login feature, parts of the page will be rendered conditionally based on the current user's state. Perhaps it will display their username in the header and show a logout button for logged-in users. 14 | 15 | I usually build my sites out at first without any auth and then add auth at a later stage. If I'm using a common layout, I would later need to add something like the following to the handler of every route. 16 | 17 | ```js 18 | handler: function (request, reply) { 19 | ... 20 | reply.view('index', { 21 | credentials: request.auth.credentials 22 | }); 23 | } 24 | ``` 25 | 26 | And in my template I might have something like: 27 | 28 | {{#if credentials.firstName}} 29 |

Welcome back {{credentials.firstName}}!

30 | {{else}} 31 |

Welcome guest!

32 | {{/if}} 33 | 34 | This module saves the work by ensuring `request.auth.credentials` is included in every view context on your server, so you don't need to manually include it in your handlers. 35 | 36 | ##Usage 37 | 38 | Just register like any Hapi plugin: 39 | 40 | ```js 41 | server.register(require('hapi-context-credentials'), (err) => { 42 | 43 | if (err) { 44 | throw err; 45 | } 46 | 47 | server.start(function (err) { 48 | 49 | if (err) { 50 | throw err; 51 | } 52 | console.log('Server started!'); 53 | }); 54 | }); 55 | ``` 56 | 57 | You can then output `credentials` in your views. 58 | 59 | ##Credit 60 | 61 | The idea for this plugin came from https://github.com/hapijs/hapi/issues/2419. Credit to @ubaltaci for the preResponse handler idea. 62 | -------------------------------------------------------------------------------- /examples/basic/home.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello! 6 | 7 | 8 | {{#if credentials}} 9 |

Hello {{credentials.username}}!

10 | {{else}} 11 |

Hello guest!

12 | You can login here 13 | {{/if}} 14 | 15 | -------------------------------------------------------------------------------- /examples/basic/index.js: -------------------------------------------------------------------------------- 1 | var Hapi = require('hapi'); 2 | var Path = require('path'); 3 | 4 | var server = new Hapi.Server(); 5 | server.connection({ port: 4000 }); 6 | 7 | server.register([ 8 | { register: require('vision') }, 9 | { register: require('hapi-auth-basic') }, 10 | { register: require('../../index') } 11 | ], function (err) { 12 | 13 | if (err) { 14 | throw err; 15 | } 16 | 17 | server.views({ 18 | engines: { 19 | hbs: require('handlebars') 20 | }, 21 | path: __dirname, 22 | isCached: false 23 | }); 24 | 25 | var validateFunc = function (request, username, password, callback) { 26 | 27 | // Just authenticate everyone and store username 28 | // in credentials 29 | 30 | if (username === 'john' && password === 'secret') { 31 | return callback(null, true, { username: 'john' }); 32 | } 33 | 34 | return callback(null, false, {}); 35 | }; 36 | 37 | server.auth.strategy('simple', 'basic', { 38 | validateFunc: validateFunc 39 | }); 40 | 41 | server.route([ 42 | { 43 | config: { 44 | auth: { 45 | strategy: 'simple', 46 | mode: 'try' 47 | } 48 | }, 49 | method: 'GET', 50 | path: '/', 51 | handler: function (request, reply) { 52 | 53 | reply.view('home'); 54 | } 55 | }, 56 | { 57 | config: { 58 | auth: { 59 | strategy: 'simple' 60 | } 61 | }, 62 | method: 'GET', 63 | path: '/login', 64 | handler: function (request, reply) { 65 | 66 | return reply.redirect('/'); 67 | } 68 | } 69 | ]); 70 | 71 | server.start(function () { 72 | 73 | console.log('Started serverx'); 74 | }); 75 | }); 76 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib'); 2 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | exports.register = function (server, options, next) { 2 | 3 | server.ext('onPreResponse', function (request, reply) { 4 | 5 | var response = request.response; 6 | if (response.variety && response.variety === 'view') { 7 | response.source.context = response.source.context || {}; 8 | response.source.context.credentials = request.auth.isAuthenticated ? request.auth.credentials : null; 9 | } 10 | return reply.continue(); 11 | }); 12 | 13 | next(); 14 | }; 15 | 16 | exports.register.attributes = { 17 | pkg: require('../package.json') 18 | }; 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hapi-context-credentials", 3 | "version": "2.0.0", 4 | "description": "Include `request.auth.credentials` in template view context", 5 | "main": "index.js", 6 | "author": "Matt Harrison", 7 | "license": "BSD-3-Clause", 8 | "scripts": { 9 | "test": "lab -a code -t 100 -L", 10 | "test-cov-html": "lab -a code -r html -o coverage.html" 11 | }, 12 | "peerDependencies": { 13 | "hapi": ">=8.x.x" 14 | }, 15 | "devDependencies": { 16 | "code": "^1.5.0", 17 | "handlebars": "3.x.x", 18 | "hapi": "9.x.x", 19 | "hapi-auth-basic": "3.x.x", 20 | "lab": "^5.15.2", 21 | "vision": "^3.0.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | // Load modules 2 | 3 | var Code = require('code'); 4 | var Hapi = require('hapi'); 5 | var Lab = require('lab'); 6 | 7 | 8 | // Declare internals 9 | 10 | var internals = {}; 11 | 12 | 13 | // Test shortcuts 14 | 15 | var lab = exports.lab = Lab.script(); 16 | var describe = lab.describe; 17 | var it = lab.it; 18 | var expect = Code.expect; 19 | 20 | 21 | it('doesn\'t interfere with non view responses', function (done) { 22 | 23 | var server = new Hapi.Server(); 24 | server.connection(); 25 | server.register(require('../'), function (err) { 26 | 27 | expect(err).to.not.exist(); 28 | 29 | server.route({ 30 | method: 'GET', 31 | path: '/', 32 | handler: function (request, reply) { 33 | 34 | return reply('ok'); 35 | } 36 | }); 37 | 38 | var request = { method: 'GET', url: '/' }; 39 | 40 | server.inject(request, function (res) { 41 | 42 | expect(res.result).to.equal('ok'); 43 | done(); 44 | }); 45 | }); 46 | }); 47 | 48 | it('doesn\'t include credentials if not authenticated', function (done) { 49 | 50 | var server = new Hapi.Server(); 51 | server.connection(); 52 | server.register([require('../'), require('vision')], function (err) { 53 | 54 | expect(err).to.not.exist(); 55 | 56 | server.views({ 57 | engines: { 58 | hbs: require('handlebars') 59 | }, 60 | path: __dirname, 61 | isCached: false 62 | }); 63 | 64 | server.route({ 65 | method: 'GET', 66 | path: '/', 67 | handler: function (request, reply) { 68 | 69 | request.auth.credentials = {}; 70 | request.auth.credentials.username = 'john'; 71 | return reply.view('test'); 72 | } 73 | }); 74 | 75 | var request = { method: 'GET', url: '/' }; 76 | 77 | server.inject(request, function (res) { 78 | 79 | expect(res.result).to.equal('Hello !'); 80 | done(); 81 | }); 82 | }); 83 | }); 84 | 85 | it('includes credentials if authenticated', function (done) { 86 | 87 | var server = new Hapi.Server(); 88 | server.connection(); 89 | server.register([require('../'), require('vision')], function (err) { 90 | 91 | expect(err).to.not.exist(); 92 | 93 | server.views({ 94 | engines: { 95 | hbs: require('handlebars') 96 | }, 97 | path: __dirname, 98 | isCached: false 99 | }); 100 | 101 | server.route({ 102 | method: 'GET', 103 | path: '/', 104 | handler: function (request, reply) { 105 | 106 | request.auth.isAuthenticated = true; 107 | request.auth.credentials = {}; 108 | request.auth.credentials.username = 'john'; 109 | return reply.view('test'); 110 | } 111 | }); 112 | 113 | var request = { method: 'GET', url: '/' }; 114 | 115 | server.inject(request, function (res) { 116 | 117 | expect(res.result).to.equal('Hello john!'); 118 | done(); 119 | }); 120 | }); 121 | }); 122 | 123 | it('merges credentials with existing context', function (done) { 124 | 125 | var server = new Hapi.Server(); 126 | server.connection(); 127 | server.register([require('../'), require('vision')], function (err) { 128 | 129 | expect(err).to.not.exist(); 130 | 131 | server.views({ 132 | engines: { 133 | hbs: require('handlebars') 134 | }, 135 | path: __dirname, 136 | isCached: false 137 | }); 138 | 139 | server.route({ 140 | method: 'GET', 141 | path: '/', 142 | handler: function (request, reply) { 143 | 144 | request.auth.isAuthenticated = true; 145 | request.auth.credentials = {}; 146 | request.auth.credentials.username = 'john'; 147 | return reply.view('test', { a: 1 }); 148 | } 149 | }); 150 | 151 | var request = { method: 'GET', url: '/' }; 152 | 153 | server.inject(request, function (res) { 154 | 155 | expect(res.result).to.equal('Hello john!'); 156 | done(); 157 | }); 158 | }); 159 | }); 160 | 161 | it('does\'t affect non-variety responses', function (done) { 162 | 163 | var server = new Hapi.Server(); 164 | server.connection(); 165 | server.register([require('../'), require('vision')], function (err) { 166 | 167 | expect(err).to.not.exist(); 168 | 169 | server.route({ 170 | method: 'GET', 171 | path: '/', 172 | handler: function (request, reply) { 173 | 174 | reply(new Error('error')); 175 | } 176 | }); 177 | 178 | var request = { method: 'GET', url: '/' }; 179 | 180 | server.inject(request, function (res) { 181 | 182 | expect(res.statusCode).to.equal(500); 183 | done(); 184 | }); 185 | }); 186 | }); 187 | 188 | internals.header = function (username, password) { 189 | 190 | return 'Basic ' + (new Buffer(username + ':' + password, 'utf8')).toString('base64'); 191 | }; 192 | -------------------------------------------------------------------------------- /test/test.hbs: -------------------------------------------------------------------------------- 1 | Hello {{credentials.username}}! --------------------------------------------------------------------------------