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