I'm following this tutorial to implement jwt authentication in hapijs v17.2.
I did everything according to the tutorial, but the following error is driving me crazy, even debugging didn't make any change.
error
Debug: internal, implementation, error
    TypeError: cb is not a function
    at Object.secretProvider [as key] (C:\Users\user\WebstormProjects\hapi-blog\node_modules\jwks-rsa\lib\integrations\hapi.js:30:14)
    at Object.authenticate (C:\Users\user\WebstormProjects\hapi-blog\node_modules\hapi-auth-jwt2\lib\index.js:123:87)
    at module.exports.internals.Manager.execute (C:\Users\user\WebstormProjects\hapi-blog\node_modules\hapi\lib\toolkit.js:35:106)
    at module.exports.internals.Auth._authenticate (C:\Users\user\WebstormProjects\hapi-blog\node_modules\hapi\lib\auth.js:242:58)
    at authenticate (C:\Users\user\WebstormProjects\hapi-blog\node_modules\hapi\lib\auth.js:217:21)
    at module.exports.internals.Request._lifecycle (C:\Users\user\WebstormProjects\hapi-blog\node_modules\hapi\lib\request.js:261:62)
    at <anonymous>
app.js
const hapi = require('hapi');
const mongoose = require('./db');
const hapi_auth_jwt = require('hapi-auth-jwt2');
const jwksa_rsa = require('jwks-rsa');
const dog_controller = require('./controllers/dog');
const server = new hapi.Server({
    host: 'localhost',
    port: 4200
});
const validate_user = (decoded, request, callback) => {
    console.log('Decoded', decoded);
    if (decoded && decoded.sub) {
        return callback(null, true, {});
    }
    return callback(null, true, {});
};
const register_routes = () => {
    server.route({
        method: 'GET',
        path: '/dogs',
        options: {
            handler: dog_controller.list,
            auth: false
        }
    });
    // Test
    server.route({
        method: 'POST',
        path: '/a',
        options: {
            handler: (req, h) => {
                return h.response({message: req.params.a});
            },
            auth: false
        }
    });
    server.route({
        method: 'GET',
        path: '/dogs/{id}',
        options: {
            handler: dog_controller.get
        }
    });
    server.route({
        method: 'POST',
        path: '/dogs',
        options: {
            handler: dog_controller.create
        }
    });
    server.route({
        method: 'PUT',
        path: '/dogs/{id}',
        handler: dog_controller.update
    });
    server.route({
        method: 'DELETE',
        path: '/dogs/{id}',
        handler: dog_controller.remove
    });
};
const init = async () => {
    await server.register(hapi_auth_jwt);
    server.auth.strategy('jwt', 'jwt', {
        key: jwksa_rsa.hapiJwt2Key({
            cache: true,
            rateLimit: true,
            jwksRequestsPerMinute: 5,
            // YOUR-AUTH0-DOMAIN name e.g https://prosper.auth0.com
            jwksUri: 'https://mrvar.auth0.com/.well-known/jwks.json'
        }),
        verifyOptions: {
            audience: 'https://mrvar.auth0.com/api/v2/',
            issuer: 'https://mrvar.auth0.com',
            algorithm: ['RS256']
        },
        validate: validate_user
    });
    server.auth.default('jwt');
    // Register routes
    register_routes();
    // Start server
    await server.start();
    return server;
};
init().then(server => {
    console.log('Server running at: ', server.info.uri);
}).catch(err => {
    console.log(err);
});
When I make a request to routes with auth: false, the handler works properly then I get the expected result, but requests to routes without auth return the following json :
{
    "statusCode": 500,
    "error": "Internal Server Error",
    "message": "An internal server error occurred"
}
More info:
node version: 8.9.4
npm version: 5.6.0
hapi version: 17.2.0
hapi-auth-jwt2: github:salzhrani/hapi-auth-jwt2#v-17
jwks-rsa: 1.2.1
mongoose: 5.0.6
nodemon: 1.15.0
I also faced this issue, but surprisingly both libraries has support for hapi v.17, but all documentation is based on old versions or didn't use this combination ;)
There are few things that has to be changed then using hapijs v.17
"jwks-rsa": "^1.3.0","hapi-auth-jwt2": "^8.0.0",hapiJwt2KeyAsync instead of hapiJwt2Key.Information about this new async method is hidden in node-jwks-rsa package documentation
validate function has now new contractPlease change your existing validate function to below type:
async (decoded: any, request: hapi.Request): {isValid: boolean, credentials: {}} 
const validateUser = async (decoded, request) => {
    if (decoded && decoded.sub) {
        return decoded.scope
            ? {
                isValid: true,
                credentials: {
                    scope: decoded.scope.split(' ')
                }
            }
            : { isValid: true };
    }
    return { isValid: false };
};
server.auth.strategy('jwt', 'jwt', {
    complete: true,
    key: jwksRsa.hapiJwt2KeyAsync({
        cache: true,
        rateLimit: true,
        jwksRequestsPerMinute: 5,
        jwksUri: env.auth.jwksUri
    }),
    verifyOptions: {
        audience: '/admin',
        issuer: env.auth.issuer,
        algorithms: ['RS256']
    },
    validate: validateUser
});
The validate function changed in hapi@17 to not have a callback function. Based on your example it should now look something like this:
const validate = async (decoded, request) => {
  if (decoded && decoded.sub) {
     return { isValid: true };
  }
  return { isValid: false };
};
Part of the returned object can also include credentials which would represent the user that is authenticated and you can also do a scope as part of the credentials. 
Then if you want to you can access the credentials as part of the request object like request.auth.credentials
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With