Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integration with glue #151

Closed
no-more opened this issue Mar 11, 2016 · 31 comments
Closed

Integration with glue #151

no-more opened this issue Mar 11, 2016 · 31 comments

Comments

@no-more
Copy link

no-more commented Mar 11, 2016

Hello,

Sorry for this newbie question but I couldn't figure out how to register and configure this plugin with glue manifest. I don't know what to put in options, I don't even know it this is possible with glue?

Thanks.

@nelsonic
Copy link
Member

@no-more great question, really sorry that is unclear from our Docs.
At present this plugin does not support glue out-of-the-box.
are you using glue in your project...?

(only one other person has requested glue support, so it has not yet been a priority for us...)

@vdeturckheim
Copy link

I don't see any straightforward way to do this using Glue.
The solution I use and recommend is to build a plugin around hapi-auth-jwt2.
Take a look at this repo: https://github.com/vdeturckheim/sticky_auth
I created a plugin around hapi-auth-basic to assess my needs.

@vdeturckheim
Copy link

@nelsonic a glue usage out of the box seems complicated to me. At least it will need a very careful design since everyone is using a different database or have different need (I decorate my auth objects with scopes for instance).
A solution I see would be to let the conf object accept the reference to a node module exposing the functions needed:

{
    "functionsHome": "myFcts"
}

with myFcts being a module exposing a validate function.

However I see 2 enhancements we could easely make here:

  • let use redis out of the box (just provide access to instance)
  • allow to drop the following lines in favor of configuration:

    server.auth.strategy('jwt', 'jwt',
    { key: 'NeverShareYourSecret',          // Never Share your secret key
      validateFunc: validate,            // validate function defined above
      verifyOptions: { algorithms: [ 'HS256' ] } // pick a strong algorithm
    });

    server.auth.default('jwt');

@no-more
Copy link
Author

no-more commented Mar 11, 2016

Hi,

thanks for your fast answers, yes I'm currently using glue and try to put every plugin configuration within it.
I'm building it with a manifest.js file which allow me to put stuff dynamically within it, but I'm not sure if it's possible to define function in the final manifest file.
I think, but I'm not sure, I've seen something like this somewhere but can't find it anymore.

I'm quite new to nodejs & hapi, I'm just trying to build a boilerplate for my next project so I'm trying to find best practices, that's why I'm trying to use glue which seems to be the new way to do things in hapi.

Thanks.

@vdeturckheim
Copy link

The only time I've seen functions in manifest would be here https://github.com/hapijs/rejoice
There is a possibility for preConnections and preRegister method.
You can take a look at my sticky repos. I created them to show the use of Glue to @nelsonic actually ;)

@Miguel-Herrero
Copy link

Hi, I was considering using this plugin for my hapijs project, but as I'm using Glue and I read here that it's not supported, I don't know if I can plug it easily…

(I started using Glue because the official hapijs tutorial teaches to use it…)

@nelsonic
Copy link
Member

@Miguel-Herrero glue is nice but fewer than 8% of Hapi projects use it...

@Miguel-Herrero
Copy link

Oh, I didn't know that @nelsonic Thanks for the info.

I guess a hapijs app is more configurable without Glue?

@nelsonic
Copy link
Member

@Miguel-Herrero glue is a really nice way of composing your Hapi.js app.
Its just not used by that many apps/teams... I have been tempted to use it, but feel that I don't need an additional layer of abstraction when Hapi already allows me to write my app declaratively...
However, we could add glue support if more people request it...

@mikefrey
Copy link

mikefrey commented May 3, 2016

I'd love to see glue support. We use rejoice which uses glue internally. It's a great way to force you to write everything in plugins, which in turn, forces you to design with modularity and maintainability in mind.

@avanslaars
Copy link

avanslaars commented May 10, 2016

For what it's worth, I'd love to see glue support as well. I'm using it at work and on a personal project. If I find some free time and this still isn't a high priority, I may take a stab at this.

For the time being, I'm taking the advice of @vdeturckheim and have simply wrapped this in my own plugin like so:

'use strict'

const hapiJwt = require('hapi-auth-jwt2')
const JWT_SECRET_KEY = require('lg/lib/always').JWT_SECRET_KEY

const register = function register (server, options, next) {
  server.register(hapiJwt)
  server.auth.strategy('jwt', 'jwt',
  { key: JWT_SECRET_KEY,
    validateFunc: (decoded, request, callback) => callback(null, true), // This should be replaced with a more robust function
    verifyOptions: { algorithms: [ 'HS256' ] }
  })

  server.auth.default('jwt')
  next()
}

register.attributes = {
  name: 'auth-wrapper',
  version: '0.0.1'
}

module.exports = register

and then I can load this in my glue manifest like so:

...
registrations: [
    {
      plugin: {
        register: 'vision'
      }
    },
    {
      plugin: {
        register: './authWrapper'
      }
    },
    {
      plugin: {
        register: './PluginWithMyRoutesAndStuff'
      },
      options: {
        select: ['web']
      }
    }
...

@sean-hill
Copy link

sean-hill commented May 22, 2016

@avanslaars I tried using your idea with glue, and I got my route to return a 401 response. However, I can't get the validateFunc to run or return true and allow access to my route. Have you had any luck there?

For testing I'm just using validateFunc: (decoded, request, callback) => callback(null, true) like your example.

@avanslaars
Copy link

@sean-hill It is working in my app. It's a WIP, so I haven't done anything with my validateFunc to change it from the example. I auth with githuub's OAuth api and create a token with the jsonwebtoken module. I am storing the auth token in a cookie called token. I then use the jwt auth strategy in my routes and it accepts the token.

// Create the token

const jwt = require('jsonwebtoken')
const JWT_SECRET_KEY = require('lg/lib/always').JWT_SECRET_KEY

// Code that handles OAuth, saving user in DB, etc...

const theToken = jwt.sign({name:'sample'}, JWT_SECRET_KEY)
return reply.redirect('/dashboard').state('token', theToken, {
   path: '/',
   ttl: 2592e5
})

Sample route that uses auth

server.route([{
    method: 'GET',
    path: '/dashboard',
    config: {
      auth: 'jwt'
    },
    handler: function (request, reply) {
      reply.view('dashboard')
    }
  }

If I have the cookie in place, this route works and if I delete the cookie, I get a 401.

I hope this helps.

@sean-hill
Copy link

@avanslaars thanks. I got it working :)

@saurabhghewari
Copy link

saurabhghewari commented Jun 11, 2016

@avanslaars thanks for the answer. Your solution is working for me. But it throws error when test cases are executed. It throws.
"Multiple callbacks or thrown errors received in test "Simple test1"
Below is sample test case

 var Code = require('code');
 var Lab = require('lab');
 const Path = require( 'path' );
 const Glue = require( 'glue' );

// Test shortcuts
var lab = exports.lab = Lab.script();
var describe = lab.describe;
var it = lab.it;
var expect = Code.expect;

import Manifest from '../../src/config/manifest';

const composeOptions = {
    relativeTo: __dirname + '/../../src'
}

 class ApiServer {
    private static apiServer;

        public static getServer(callback) {

              if(ApiServer.apiServer) return callback(ApiServer.apiServer);

                   Glue.compose(new Manifest().get("/"), composeOptions, function(err, server) {

                        if (err) throw err;

                         ApiServer.apiServer = server.select('web');
                         return callback(ApiServer.apiServer);
              });
       }
  }

lab.experiment('Simple', () => {

lab.test('test1', (done) => {

    ApiServer.getServer((server) => {                                    
        server.inject({
            method: 'GET',
            url: '/api/v1/tasks'
        }, (response) => {

            Code.expect(response.statusCode).to.equal(200);
            Code.expect(response.result).to.be.an.object();
            done();
        });
    });
});

lab.test('test2', (done) => {

    ApiServer.getServer((server) => {                                    
        server.inject({
            method: 'GET',
            url: '/api/v1/tasks'
        }, (response) => {

            Code.expect(response.statusCode).to.equal(200);
            Code.expect(response.result).to.be.an.array();
            Code.expect(response.result[0]).to.be.an.object();
            done();
        });
    });
});

And auth plugin code is as you shown above.

If auth plugin is removed, above error vanishes, and registering auth plugin throws it. Running main server file does not throw this error, it works fine. Running only test cases throws this error.
Can you let me know where my code goes wrong?

@sean-hill
Copy link

@saurabhghewari I've been able to get this plugin to work well with unit tests. Here is a link to my repo. I have an app.js file which initializes the hapi server via Glue. I require this file in my unit tests and initialize the server before the tests run. My /user/get route is the one using the jwt2 plugin and they are working fine. Take a look!

@saurabhghewari
Copy link

saurabhghewari commented Jun 12, 2016

Thank you for your reply. I solved it. removed the next() from the private method of plugin in which JWT2 plugin is being registered.
@sean-hill Thanks for posting the link, got intro to studio.js. Will definitely give a try.

@sean-hill
Copy link

@nelsonic I believe this issue is closed with @avanslaars example.

@saurabhghewari
Copy link

Yes for sure!!!

@nelsonic
Copy link
Member

@sean-hill looks like it! Do you think we should add a link in our Readme FAQ section to @avanslaars example so others looking for how to use glue will know where to find it?

@sean-hill
Copy link

@nelsonic I think that would be great idea.

@Mikjail
Copy link

Mikjail commented Mar 18, 2018

@avanslaars I did the same than you but I get this error:

"Authentication strategy jwt uses unknown scheme: jwt"

(n)

@saurabhghewari
Copy link

@Mikjail I guess you are getting this error because, some where in the project the auth scheme is used before the plugin is registered. The registration sequence matters while registering the plugins.

Sequence should be:-

  1. Common plugins which are required all over the app.
  2. User defined plugins or plugins using the common plugins.

Hope you get this.

@supun19
Copy link

supun19 commented Jul 25, 2018

@Mikjail did you fix the error. i alse get the same error. can you help me?

@saurabhghewari
Copy link

@supun19 can you post some code here so that I can help you out.

@supun19
Copy link

supun19 commented Jul 26, 2018

@Mikjail thank you your reply

I am using this framework https://github.com/jedireza/aqua

this is a manifest.json

`'use strict';
const Confidence = require('confidence');
const Config = require('./config.js');

const criteria = {
env: process.env.NODE_ENV
};

const manifest = {
$meta: 'This file defines the plot device.',
server: {
debug: {
request: ['error']
},
connections: {
routes: {
security: true
}
}
},
connections: [{
port: Config.get('/port/web'),
labels: ['web'],
state: {
isHttpOnly: false,
isSecure: {
$filter: 'env',
production: true,
$default: false
}
}
}],
registrations: [
{
plugin: 'inert'
},
{
plugin: {
register: 'crumb',
options: {
restful: true
}
}
},
{
plugin: 'vision'
},
{
plugin: {
register: 'visionary',
options: {
engines: { jsx: 'hapi-react-views' },
compileOptions: {
removeCacheRegExp: '.jsx'
},
relativeTo: __dirname,
path: './server/web'
}
}
},
{
plugin: './server/auth'
},
{
plugin: './server/web/classroom'
},
{
plugin: './server/web/public'
}
]
};

const store = new Confidence.Store(manifest);

exports.get = function (key) {

return store.get(key, criteria);

};

exports.meta = function (key) {

return store.meta(key, criteria);

};
`

this is a auth.js

`'use strict';
const Async = require('async');
const Boom = require('boom');
const Config = require('../config');

const internals = {};

const people = { // our "users database"
1: {
id: 1,
name: 'Jen Jones'
}
};

// bring your own validation function
const validate = function (decoded, request) {

// do your checks to see if the person is valid
if (!people[decoded.id]) {
    return { isValid: false };
}
else {
    return { isValid: true };
}

};

internals.applyStrategy = function (server, next) {

server.auth.strategy('token', 'jwt',
    { key: 'NeverShareYourSecret',          // Never Share your secret key
        validate: validate,            // validate function defined above
        verifyOptions: { algorithms: [ 'HS256' ] } // pick a strong algorithm
    });

server.auth.default('jwt');

next();

};

exports.register = function (server, options, next) {

server.dependency('hapi-auth-jwt2', internals.applyStrategy);

next();

};

exports.register.attributes = {
name: 'auth'
};
`

this is a route
`'use strict';

const internals = {};

internals.applyRoutes = function (server, next) {

server.route({
    method: 'GET',
    path: '/classroom/{glob*}',
    config: {
        auth: {
            strategy: 'token'
        }
    },
    handler: function (request, reply) {

        reply.view('classroom/index');
    }
});


next();

};

exports.register = function (server, options, next) {

server.dependency([], internals.applyRoutes);

next();

};

exports.register.attributes = {
name: 'web/classroom'
};
`

@supun19
Copy link

supun19 commented Aug 8, 2018

@saurabhghewari can you help me, please. I am stuck in this.

@saurabhghewari
Copy link

Hi Supun19, Sorry for the late reply.
change server.auth.default('jwt') to server.auth.default('token');
Also you have got incorrect server.dependency('hapi-auth-jwt2', internals.applyStrategy). Self dependency cannot be resolved here. server dependency document

@supun19
Copy link

supun19 commented Aug 9, 2018

@saurabhghewari thank you

@saurabhghewari
Copy link

Did that solve your problem?

@supun19
Copy link

supun19 commented Aug 10, 2018

@saurabhghewari thank you for your reply
we decoded token using https://github.com/auth0/node-jsonwebtoken in router

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

10 participants