Why does this verified JSON Web Token (JWT) output as undefined? - javascript

I'm trying to decode a JWT id_token using jwks-rsa and jsonwebtoken but the result is returning as undefined.
I know this has something to do with callbacks, and the fact that I need to wait for a response from the getKey function but I can't wrap my head around how to structure the code to make that happen.
This is what I have so far...
function do_thing(properties, context) {
const id_token = "REDACTED";
// Verify using getKey callback
var jwksClient = require('jwks-rsa');
var client = jwksClient({
jwksUri: 'https://REDACTED.com/.well-known/jwks.json'
});
function getKey(header, callback) {
client.getSigningKey(header.kid, function(err, key) {
var signingKey = key.publicKey || key.rsaPublicKey;
callback(null, signingKey);
});
}
var jwt = require('jsonwebtoken');
jwt.verify(id_token, getKey, { algorithms: ['RS256'] }, function(err, decoded) {
if (err) {
console.log(err);
} else {
return decoded;
}
});
const bubble_obj = do_thing();
console.log(bubble_obj); //This is `undefined`
The console.log(bubble_obj); outputs as undefined.
I know the problem with the above code is due to the nature of callbacks and asynchronous code, because if I move the console.log inside the jwt.verify call it will show the correctly decoded token.
See here for that example...
function do_thing(properties, context) {
const id_token = "REDACTED";
// Verify using getKey callback
var jwksClient = require('jwks-rsa');
var client = jwksClient({
jwksUri: 'https://REDACTED.com/.well-known/jwks.json'
});
function getKey(header, callback) {
client.getSigningKey(header.kid, function(err, key) {
var signingKey = key.publicKey || key.rsaPublicKey;
callback(null, signingKey);
});
}
var jwt = require('jsonwebtoken');
jwt.verify(id_token, getKey, { algorithms: ['RS256'] }, function(err, decoded) {
if (err) {
console.log(err);
} else {
console.log(decoded); //When moved here, it prints the correctly decoded token
return decoded;
}
});
const bubble_obj = do_thing();
So how do I make it return the correctly decoded token?

You're not handling the asynchronous code correctly. The jwt.verify method returns a Promise if you do not pass it the callback method.
If you use return jwt.verify(id_token, getKey, { algorithms: ['RS256'] }) inside the do_thing function and call it like this do_thing().then((decodedToken) => console.log(decodedToken)), it should work as expected.

you need to handle promise returned by jwt.verify. either use promise.then. or go with async/await.
async function do_thing(properties, context) {
const id_token = "REDACTED";
// Verify using getKey callback
var jwksClient = require('jwks-rsa');
var client = jwksClient({
jwksUri: 'https://REDACTED.com/.well-known/jwks.json'
});
function getKey(header, callback) {
client.getSigningKey(header.kid, function(err, key) {
var signingKey = key.publicKey || key.rsaPublicKey;
callback(null, signingKey);
});
}
const jwt = require('jsonwebtoken');
return jwt.verify(id_token, getKey, { algorithms: ['RS256'] });
}
const decodedToken = await do_thing();
console.log("decoded token:", decodedToken);

You can use a promise to verify the JWT with a JWK callback and promise as follows. You will need to wrap the following in an async function to use the result of the verify_jwks() function:
const token = "REDACTED";
var jwksClient = require('jwks-rsa');
// Creates a JWKS Client with a rate limit that
// limits the number of calls to our JWKS endpoint
var client = new JwksClient({
jwksUri: 'https://REDACTED.com/.well-known/jwks.json',
rateLimit: true,
jwksRequestsPerMinute: 10, // Default Value
cache: true, // Default Value
cacheMaxEntries: 5, // Default value
cacheMaxAge: 600000, // Defaults to 10m
});
// Verifies the JWKS asynchronously, returns Promise
async function verify_jwks() {
function getKey(header, callback) {
// Callback that returns the key the corresponding key[kid]
client.getSigningKey(header.kid, function(err, key) {
const signingKey = key.getPublicKey() || key.publicKey || key.rsaPublicKey;
callback(null, signingKey);
});
}
// Returns a Promise with verification result or error
return new Promise((resolve,reject) =>
jsonwebtoken.verify(token,getKey, {
algorithms: ["HS256", "RS256"]
},
function(err,decoded) {
return err ? reject(err) : resolve(decoded);
}
));
}
let result;
await verify_jwks()
.then(decoded => result = decoded)
.catch(error => console.log(error));
console.log(result);

Related

Async/Await : called function is not awiting and returning value

Hello I am creating 1 function with dynamic arguments where as I am calling api and on defined route I am calling express middleware function and from there I am calling another dynamic function which will help me to insert data into the database.
I am using Sequalize ORM
Here is code:
var async = require('async');
// Models
var LogSchema = require('../models/Logs')
module.exports = {
insertLog: async (req, res) => {
let result = await insertLogFn('1', 'method_name()', 'module_name_here', 'req.body', '{ api response }', 'action', '24')
console.log("result", result)
res.status(200).json(result)
}
};
function insertLogFn(status, invokedMethodName, moduleName, bodyRequest, apiResponse = null, actionName = null, userId) {
async.waterfall([
(nextCall) => {
let dataToBeInserted = {}
dataToBeInserted.status = status,
dataToBeInserted.invoked_method_name = invokedMethodName,
dataToBeInserted.module_name = moduleName,
dataToBeInserted.body_request = bodyRequest,
dataToBeInserted.api_response = apiResponse
dataToBeInserted.action_name = actionName,
dataToBeInserted.user_id = userId
LogSchema.create(dataToBeInserted).then(res => {
const dataObj = res.get({plain:true})
nextCall(null, {
status: 200,
message: "Log inserted successfully",
data: dataObj
})
}).catch(err => {
})
}
], (err, response) => {
if(err) {
}
return response
})
}
In module.export I have added insertLog function which is getting called in api and from there I am calling insertLogFn() which is declared outside of the module.export.
I am able to get inserted result in function insertLogFn() but the things is await is not working and not waiting for the result.
What I want to do is to wait till insertLogFn gets executed and the returned response has to be stored in the variable and return it as an api response.
You cannot. As per my understanding, IMO, Thumb rule is "Async/Await operation should return a promise"
function insertLogFn(status, invokedMethodName, moduleName, bodyRequest, apiResponse = null, actionName = null, userId) {
async.waterfall([
(nextCall) => {
let dataToBeInserted = {}
dataToBeInserted.status = status,
dataToBeInserted.invoked_method_name = invokedMethodName,
dataToBeInserted.module_name = moduleName,
dataToBeInserted.body_request = bodyRequest,
dataToBeInserted.api_response = apiResponse
dataToBeInserted.action_name = actionName,
dataToBeInserted.user_id = userId
LogSchema.create(dataToBeInserted).then(res => {
const dataObj = res.get({plain:true})
nextCall(null, {
status: 200,
message: "Log inserted successfully",
data: dataObj
})
return ;
console.log("you should return something here<-------");
}).catch(err => {
})
}
], (err, response) => {
if(err) {
}
return response
})
}
Now the answer will be clear if you read this one from Bergi: https://stackoverflow.com/a/40499150/9122159

Adding a row to a google spreadsheet using google-spreadsheet node module and returning the status

I am able to successfully add a row to a google spreadsheet using the google-spreadsheet node module as follows:
const logToGoogleSpreadsheet = (userName, description, link) => {
const spreadsheetId = 'my-spreadsheet-id'
const doc = new GoogleSpreadsheet(`${spreadsheetId}`)
const clientEmail = 'my-client-email'
const privateKey = 'my-private-key'
const payload = {
client_email: clientEmail,
private_key: privateKey
}
let status = ''
doc.useServiceAccountAuth(payload, function (err) {
doc.addRow(1, { 'Reported By': userName, 'Description': description, 'Screenshot Link': link, 'Status': 'Open' }, function(err) {
if(err) {
console.log(err);
status = 'some error'
} else {
console.log('It worked')
status = 'success'
}
});
})
return status
}
const result = logToGoogleSpreadsheet('username', 'description', 'link')
console.log(`The value of result is ${result}`) //This always shows undefined as the value
The value of result always is 'undefined'. I know this is due to the asynchronous nature of javascript and being unable to modify anything in a callback function, but im not able to fix this issue. Can someone please show me an example of how i can return the status from the logToGoogleSpreadsheet function ?
Thank You
you could do this:
const logToGoogleSpreadsheet = *async* (userName, description, link) => {//add async keyword
const spreadsheetId = 'my-spreadsheet-id'
const doc = new GoogleSpreadsheet(`${spreadsheetId}`)
const clientEmail = 'my-client-email'
const privateKey = 'my-private-key'
const payload = {
client_email: clientEmail,
private_key: privateKey
}
let status = ''
doc.useServiceAccountAuth(payload, function (err) {
doc.addRow(1, { 'Reported By': userName, 'Description': description, 'Screenshot Link': link, 'Status': 'Open' }, function(err) {
if(err) {
console.log(err);
status = 'some error'
} else {
console.log('It worked')
status = 'success'
}
});
})
return status
}
logToGoogleSpreadsheet('username', 'description', 'link').then(res => console.log(res));
adding the async keyword to logToGoogleSpreadsheet will make it return a Promise. Now it is a 'thenable' meaning the then method can be called with the resolved result, which here is your status. So in the then call we log it.

Express dosn't get return of other function querying Mongodb [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 years ago.
I'm working in a simple API Key authentication, I just want to verify the given key against the user provied key.
I have a seperate file with the function querying the database, and returning true/false and the user object.
But in my route.js file, the return object is undefined even tough in my auth.js file it isn't.
I tried making the the function in router.get an async function using express-promise-router and making the function an await return var user = await auth.verify(req.params.uid, req.get("token")) but I don't realy know how async works.
router.js
[...]
router.get('/list/:uid', function(req, res) {
var user = auth.verify(req.params.uid, req.get("token"))
console.log("User: " + user) // <-- Undefined
if (user.status) {
res.send("Success")
} else {
res.status(403)
res.json({status: 403, error: "Unkown User / Token"})
}
})
[...]
auth.js
var db = require('./db')
var ObjectId = require('mongodb').ObjectId;
module.exports = {
verify: (uid, key) => {
try {
var collection = db.get().collection('users')
const obj_id = new ObjectId(uid)
const query = { _id: obj_id }
collection.find(query).limit(1).toArray(function(err, user) {
var status = 0;
var usr = {};
if (err) {throw err}else{status=1}
if (user.length <= 0) {throw "NotExistingExc"; status = 0}else{
usr = user[0];
if (key != usr.api) status = 0
}
var returnObj = {
status: status,
user: usr
} /* --> Is {
status: 1,
user: {
_id: d47a2b30b3d2770606942bf0,
name: 'Sh4dow',
groups: [ 0 ],
api: 'YWFiMDI1MGE4NjAyZTg0MWE3N2U0M2I1NzEzZGE1YjE='
}
}
*/
return returnObj;
})
} catch (e) {
console.error(e)
return {
status: 0,
user: {},
error: e
}
}
}
}
db.js (Idk if needed)
var MongoClient = require('mongodb').MongoClient
var state = {
db: null,
}
exports.connect = function(url, done) {
if (state.db) return done()
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
if (err) return done(err)
state.db = db
done()
})
}
exports.get = function() {
return state.db.db("database")
}
exports.close = function(done) {
if (state.db) {
state.db.close(function(err, result) {
state.db = null
state.mode = null
done(err)
})
}
}
I want to have the returnObjin auth.js in the router.get of my route.js file.
Make auth.verify return a Promise which we can then await for it inside router, You can just make the callback async no need for express-promise-router
router.get('/list/:uid', async function(req, res) {
try {
var user = await auth.verify(req.params.uid, req.get("token"))
console.log("User: " + user)
if (user.status) {
res.send("Success")
} else {
res.status(403).json({status: 403, error: "Unkown User / Token"})
}
} catch (e) {
console.error(e)
res.status(/* */).json(/* */)
}
})
auth
module.exports = {
verify: (uid, key) => new Promise((resolve, reject) => {
var collection = db.get().collection('users')
const obj_id = new ObjectId(uid)
const query = { _id: obj_id }
collection.find(query).limit(1).toArray(function(err, user) {
var status = 0;
var usr = {};
if (err) {
reject(err)
return
} else {
status = 1
}
if (user.length <= 0) {
reject(new Error("NotExistingExc"))
return
} else {
usr = user[0]
if (key != usr.api) status = 0
}
var returnObj = {
status: status,
user: usr
}
resolve(returnObj);
})
}
}
In short, the reason you get undefined is because the code in auth.js is asyncronous. But you're really close. The toArray method in MongoDB returns a promise, so you need to make sure you return that promise and then use it in the router correctly.
In auth.js, make sure verify returns a promise - just add return!
return collection.find(query).limit(1).toArray(...)
And then, change your usage of the verify to the async/await you originally tried:
router.get('/list/:uid', async function(req, res) {
var user = await auth.verify(req.params.uid, req.get("token"))
// More code here...
})

How to return a value from within activedirectory method

I've got a method in a class which does query an ActiveDirectory.
Therefore I'm using 'activedirectory2' npm package.
I successfully authenticated and successfully logged my result to console.
Now that I have instanciated my class and have tried to call the method, I'm not abled to get a non-empty result.
I tried it with getters/setters to make the _result value available after instaciating the class.
I tried to solve my issue with research on asynchronous calls, but obviously wasn't able to ask the right question.
class Activedirectory
var ActiveDirectory = require("activedirectory2");
class AuthenticateWithLDAP {
constructor(user, password){
this._result = [];
this.user = user;
this.password = password;
this.config = {
url: "ldaps://someldap",
baseDN: "somebasdn",
username: this.user,
password: this.password,
filter: 'somefilter',
}
this.ad = new ActiveDirectory(this.config);
}
//Auth Method
auth() {
var result = this._result;
this.config.entryParser = function(entry,raw,callback){
if(entry.hasOwnProperty('info')) {
result.push(entry.info);
this._result = result;
}
callback(entry);
}
this.ad.authenticate(config.username, config.password, (err,auth)=>{
if (err) {
//some error handling
}
if (auth) {
this.ad.find(config,async (err, userDetails) => {
var result = this._result;
{
if (err) {
//some error handling
}
if(!userDetails) {
console.log("No users found.");
} else {
this._result = result[0]; //I want this result!
console.log('result: ', this._result);
return await this._result;
}
}
})
} else {
console.log("Authentication failed!");
}
});
}
//getter/setter
get result(){
return this._result;
}
set result(value) {
this._result.push(value);
}
}
module.exports = AuthenticateWithLDAP;
route module
const express = require('express');
const AuthwithLDAP = require('AuthenticateWithLDAP');
const router = express.Router();
router.post('/', async (req,res,next) => {
let x = async ()=> {
authwithldap = new AuthwithLDAP(req.body.user,req.body.password);
return await authwithldap.auth();
}
x().then((res)=>{
console.log('res: ', res); //always []
})
})
I expected to be able to use the _result value of AuthenticateWithLDAP class in my router.post method handler.
Actually i only get [] (empty array) in router.post.
Could you please tell me how to alter the value _result in a way, so that the instance of the class knows it and can use it outside the class itself.
Thank you very much.
Micha
I am not 100% sure but I think this should work.
In your code you cant return the result because the return is in a callback.
There are to ways to fix that.
Pass a callback to the auth() method (This is bad since callbacks suck)
Return a promise and that resolves to the result
I've decided to go for promises.
var ActiveDirectory = require("activedirectory2");
class AuthenticateWithLDAP {
constructor(user, password){
this._result = [];
this.user = user;
this.password = password;
this.config = {
url: "ldaps://someldap",
baseDN: "somebasdn",
username: this.user,
password: this.password,
filter: 'somefilter',
}
this.ad = new ActiveDirectory(this.config);
}
//Auth Method
auth() {
return new Promise((resolve, reject) => {
this.ad.authenticate(config.username, config.password, (err,auth)=>{
if (err) {
//Call reject here
}
if (auth) {
this.ad.find(config,async (err, userDetails) => {
var result = this._result;
{
if (err) {
//some error handling
}
if(!userDetails) {
console.log("No users found.");
} else {
this._result = result[0]; //I want this result!
resolve(await this._result);
}
}
})
} else {
console.log("Authentication failed!");
}
});
});
}
}
module.exports = AuthenticateWithLDAP;
const express = require('express');
const AuthwithLDAP = require('AuthenticateWithLDAP');
const router = express.Router();
router.post('/', async (req,res,next) => {
/* This code can be simplifed
let x = async () => {
authwithldap = new AuthwithLDAP(req.body.user,req.body.password);
return await authwithldap.auth();
}
x().then((res)=>{
console.log('res: ', res); //always []
})
*/
(async () => {
authwithldap = new AuthwithLDAP(req.body.user,req.body.password);
var res = await authwithldap.auth();
console.log('res: ', res);
})();
})
Could you try to add syntax "await" like this?
await x().then((res)=>{
console.log('res: ', res); //always []
})
As your "x" method is in async mode, maybe you have to wait for the Promise to be resolved...

function not returning with promise

I'm trying to call a function (refresh_access_token) from another one and create a Promise chain from that. But the return function inside refresh_access_token is not working. The refresh_access_token doesn't return to it's caller when it completes.
I'm receiving this message:
Unhandled rejection TypeError: Cannot read property 'then' of undefined
How can I fix this?
These are the 2 function code:
exports.refresh_access_token = function(environment_hash) {
var MercadoLibre = require('../../models/index').MercadoLibre;
var needle = require('needle');
const load = require('express-load');
var meli = require('mercadolibre');
var request=require('request');
const mysql = require('mysql');
const dotenv = require('dotenv').config({path: '../../.env'});
var oauth_url = 'https://api.mercadolibre.com/oauth/token';
var env_hash = environment_hash;
where = { environment_hash: env_hash };
MercadoLibre.findOne({where: where}).then(function (account) {
if (!account) {
// Item not found
} else {
// Found an item, update it
needle.post(oauth_url, {
grant_type: 'refresh_token',
client_id: process.env.MERCADOLIBRE_CLIENT_ID,
client_secret: process.env.MERCADOLIBRE_SECRET,
refresh_token: account.refresh_token
}, {
}, function (err, res, body) {
if (body) {
expires_in = new Date();
expires_in.setUTCHours(0);
expires_in.setTime(expires_in.getTime() + 21600*1000);
values = {
refresh_token: body.refresh_token,
expires_in: expires_in
};
where = { environment_hash: env_hash };
return MercadoLibre.update(values, {where: where});
}
});
}
});
}
exports.run_mercadolibre_jobs = function() {
var MercadoLibre = require('../../models/index').MercadoLibre;
var values = {
attributes: ['environment_hash'],
raw: true
};
MercadoLibre
.findAll(values)
.then(function(accounts) {
Promise.all(accounts.map(function(account) {
module.exports.refresh_access_token(account.environment_hash)
.then(function(response) {
console.log(response);
})
.catch(function(response){
console.log(response);
});
}));
});
}
Your function refresh_access_token is not returning anything. Your only return statement is inside the needle.post callback. You should be first returning:
return MercadoLibre.findOne(...);
However, you are mixing promises, with callbacks (used in your needle.port call). I would suggest reviewing Promises vs Callbacks and how to use them together. Here is a good thread on how to convert callback-apis to promises: How do I convert an existing callback API to promises?.
Another alternative would be to replace using needle with a promise-supported node library:
Axios
Request Promise

Categories