Hapi js throwing Error: Already closed - node.js

I am using Hapi.js as a framework for our API development, and i am getting following error in very rare scenario.
2015-02-08T12:32:38.073Z - verbose: err.stack > Error: Already closed
at Object.exports.create (/var/www/ragchewAppServerSrc/ragchew_prod/ragchews/node_modules/hapi/node_modules/boom/lib/index.js:21:17)
at Object.exports.internal (/var/www/ragchewAppServerSrc/ragchew_prod/ragchews/node_modules/hapi/node_modules/boom/lib/index.js:252:92)
at /var/www/ragchewAppServerSrc/ragchew_prod/ragchews/node_modules/hapi/lib/request.js:297:34
at iterate (/var/www/ragchewAppServerSrc/ragchew_prod/ragchews/node_modules/hapi/node_modules/items/lib/index.js:35:13)
at done (/var/www/ragchewAppServerSrc/ragchew_prod/ragchews/node_modules/hapi/node_modules/items/lib/index.js:27:25)
at validate (/var/www/ragchewAppServerSrc/ragchew_prod/ragchews/node_modules/hapi/lib/auth.js:283:20)
at finish (/var/www/ragchewAppServerSrc/ragchew_prod/ragchews/node_modules/hapi/lib/protect.js:45:21)
at wrapped (/var/www/ragchewAppServerSrc/ragchew_prod/ragchews/node_modules/hapi/node_modules/hoek/lib/index.js:798:20)
at root (/var/www/ragchewAppServerSrc/ragchew_prod/ragchews/node_modules/hapi/lib/auth.js:198:50)
at /var/www/ragchewAppServerSrc/ragchew_prod/ragchews/src/middlewares/auth/ragchew_auth_strategy.js:75:28
2015-02-08T12:32:38.073Z - verbose: err > {"isBoom":true,"output":{"statusCode":500,"payload":{"statusCode":500,"error":"Internal Server
Neither am able to reproduce this in our testing environment, nor i understand the root cause of this error.
It would be great help if some-one highlight why/when this error is generated by framework.
In our code this error occur when we try to send the reply back from 'Authentication plugin'. We are using basic authentication scheme.
Sample snippet where issue occurs is:
exports.register = function (plugin, options, next) {
plugin.auth.scheme('basic', function (server, options) {
var settings = options;
// some code here
var scheme = {
authenticate: function (request, reply) {
// some code here
// assign access token value to token here.
settings.validateFunc.call(request, token, function (err, isValid, credentials) {
// handle error here.
return reply(null, { credentials: credentials }); // error occurs on this line
});
}
};
return scheme;
});
next();
};

Can you try return reply.continue({credentials: credentials}); on success instead of what you have now?
See this line in hapi-auth-basic for reference:
https://github.com/hapijs/hapi-auth-basic/blob/master/lib/index.js#L83

Related

How can I fix IPC error "Error invoking remote method, an object could not be cloned" in Electron?

The whole error message is the following:
Error: Error invoking remote method 'MY-IPC-CHANNEL': Error: An object
could not be cloned. at EventEmitter.o.invoke
(electron/js2c/renderer_init.js:71)
The electron/js2c/renderer_init.js:71 line is not my original line of code, but a compiled one.
I'm trying to send a POST request in order to get my Google access token, so that I can work with Google Drive's API. Currently I'm stuck trying to communicate between the renderer process and the main process by giving the main process the code I got from Google and making it send a POST request to the auth server. I have no problem establishing the connection but when I try to do it while sending an HTTP request I get the error above.
// ******* MAIN *******
function exchangeCodeForAccessToken(code: string) {
const clientID = "My Google client ID";
const clientSecret = "My Google client secret";
const body = {
code: code,
client_id: clientID,
client_secret: clientSecret,
redirect_uri: "http://localhost:4000",
grant_type: "authorization_code",
};
const body2 = `code=${code}&
client_id=${clientID}&
client_secret=${clientSecret}&
grant_type=authorization_code`;
// return fetch("https://oauth2.googleapis.com/token", {
// method: "POST",
// body: body
// });
return axios.post("https://oauth2.googleapis.com/token", body);
}
Here's the main handle:
// ******* MAIN *******
ipcMain.handle(
OAUTH2_ACCESS_TOKEN_REQUEST_CHANNEL,
async (event, code: string) => await exchangeCodeForAccessToken(code)
);
And the renderer invoke function:
// ******* RENDERER *******
function exchangeCodeForAccessToken(code: string) {
ipcRenderer.invoke(OAUTH2_ACCESS_TOKEN_REQUEST_CHANNEL, code).then((response) => {
console.log(response);
}).catch((error) => {
//TODO Improve error handling
console.log(error);
});
}
I tried sending the request through the net module from Electron. I also tried with the electron-fetch module, which is supposed to be an Electron integrated version of Node's fetch module. And finally I tried with the axios module, but it kept throwing the same error. I thought it had something to do with object serialization through IPC but then I tried just using the function without returning its promise and the same error kept popping up. Which means that the error is not only appearing when the promise is being returned but whenever the HTTP request function is being called. I also tried sending the request with both the object version of the request and its string version, hence the body and body2.
I don't know what I'm missing, and I'm so close to integrating Google login into my desktop app.
I thought it had something to do with object serialization through IPC but then I tried just using the function without returning its promise and the same error kept popping up.
It is an IPC error. You're returning the full response object, which presumably contains some properties with methods and/or other non-cloneable values. You need to make sure that the returned value can be cloned and sent to the renderer, for example:
ipcMain.handle(
OAUTH2_ACCESS_TOKEN_REQUEST_CHANNEL,
async (event, code) => {
const response = await exchangeCodeForAccessToken(code);
const {status, data} = response;
return {status, data};
}
);
I'm not sure how you called the function in your attempt to fix this, but I just ran this in Electron and it works without issues.
EDIT: Assuming response is coming from a fetch call (use response.json() if the data is JSON):
ipcMain.handle(
OAUTH2_ACCESS_TOKEN_REQUEST_CHANNEL,
async (event, code) => {
const response = await exchangeCodeForAccessToken(code);
const data = await response.text();
return data;
}
);

strongloop loopback custom error handling

I am currently using loopback 3.2.1 The issue I am facing is unhandled error getting logged in the log files when access token expires. Doing google searches I came across this Unhandled error Here it is mentioned that we can have custom error logging middleware. I followed the instructions mentioned there and also referred the document for it. However I am getting the following error :
Error: Cannot apply .../server/middleware.staging.json: The middleware "./middleware/error-logger" in phase "final:after"is not defined in the main config.
current middleware.staging.json:
...
"final": {
"loopback#urlNotFound": {}
},
"final:after": {
"./middleware/error-logger": {},
"strong-error-handler": {
"params": {
"debug": false,
"includeStack": false,
"log": false
}
}
}
server/middleware/error-logger.js:
module.exports = function createErrorLogger(options) {
return function logError(err, req, res, next) {
// your custom error-logging logic goes here
const status = err.status || err.statusCode;
if (status >= 500) {
// log only Internal Server errors
console.log('Unhandled error for request %s %s: %s',
req.method, req.url, err.stack || err);
}
// Let the next error handler middleware
// produce the HTTP response
next(err);
};
}
What am I missing here ?
Finally after looking at the code for loading the config files of loopback figured it out. Loopback is currently reading all the default config files and then merging them with the env specific file. So any configuration changes that need to be made has to be made in the default file with the values being set in the env specific files to make the change behave as required. For example in my case I was trying to add the middleware to the middleware.live.json but did not add it to the the default middleware.json file. So while executing mergePhaseConfig() the values present in the env specific files and searched for in the default file which was not present.
Adding the middleware entry in the default file solved the issue. This is already mentioned in the documents but it skipped my mind today.

How to implement this in nodejs?

I am new to nodejs and working on a proof of concept just for fun.
Background:
I have a cloud directory of user information (like username, password and other info). This cloud directory can be used to authenticate a user only via restful API (i.e. no direct connectivity using LDAP or JDBC etc.).
Aim:
To build an LDAP interface for this cloud directory. To start with I am interested only in authentication (LDAP bind).
Intended Flow:
LDAPClient initiates a standard LDAP simple BIND request:
Host: host where my nodejs app will run
Port: 1389 (port that my nodejs app will be bound to)
Username: a user from cloud directory
Password: user's password
This request is received by my NodeJS app (I am using ldapjs module).
// process ldap bind operation
myLdapServer.bind(searchBase, function (bindReq, bindRes, next) {
// bind creds
var userDn = req.dn.toString();
var userPw = req.credentials;
console.log('bind DN: ' + req.dn.toString());
...
...
}
Within the above callback, I must use http.request to fire a restful API (POST) to the cloud directory with the details I received from the BIND request (i.e. username, password).
If restful api response status is 200 (auth success), then I must return success to the LDAPClient, else I must return invalid credentials error.
Success:
bindRes.end();
return next();
Failure:
Console.log("returning error");
return next(new ldap.InvalidCredentialsError());
Questions:
Is this possible using NodeJS? Asking because of the nesting involved as evident above (calling of REST API from within a callback). Also since this is an authentication operation, this is meant to be a blocking operation(?)
Thanks,
Jatin
UPDATE:
Thanks Klvs, my solution is more or less like the one you posted. Please have a look at the snippet below:
// do the POST call from within callback
var postRequest = https.request(postOptions, function(postResponse) {
console.log("statusCode: ", postResponse.statusCode);
if(postResponse.statusCode!=200) {
console.log("cloud authentication failed: "+postResponse.statusCode);
return next(ldapModule.InvalidCredentialsError());
} else {
postResponse.on('data', function(d) {
console.info('POST result:\n');
process.stdout.write(d);
console.info('\n\nPOST completed');
});
res.end();
return next();
}
});
// write json data
postRequest.write(postData);
postRequest.end();
postRequest.on('error', function(e) {
console.error("postRequest error occured: "+e);
});
Successful authentication works fine, however, failed authentication does not send any response back to the LDAPClient at all. My client just times out instead of showing authentication failure error. I do see the "cloud authentication failed: " log message on the Node console, which means the below statement is not doing what I intend it do:
return next(ldapModule.InvalidCredentialsError());
Note that the above statement works when I remove the rest call etc, and just return the error back to the client.
Am I missing something?
Thanks,
Jatin
Of course it's possible in nodejs. If I understand you want to make an authenticating request to a server and have it either fail or succeed.
const request = require('request')
// process ldap bind operation
myLdapServer.bind(searchBase, function (bindReq, bindRes, next) {
// bind creds
var userDn = req.dn.toString();
var userPw = req.credentials;
console.log('bind DN: ' + req.dn.toString());
request.post({username: userDn, password: userPw}, (err, res, body)=>{
if(err) {
console.log("returning error");
next(new ldap.InvalidCredentialsError());
} else {
bindRes.end();
next();
}
})
}
Is that what you're looking for? If so, you just need to get accustom to callbacks.

Node/Express - Can't set headers after they are sent

I have been dealing with this problem now for quite a while, and I can't seem to figure out why it's happening.
I'm getting the Error: Can't set headers after they are sent.
I was able to track down the offending Function Call via the Stack Trace, which leads me to believe that the error lies within this function:
exports.getCardUser = function(req, res, next) {
if (req.user) {
User.cardUser(req.user.userId, function(responseValue) {
res.json(200, responseValue);
});
} else {
res.send(401);
}
};
However, if I hit the Endpoint via a regular REST Client, iex: Hitting the API Endpoint in an Isolated Environment, the Error is not being thrown.
Any ideas?
Edit: skypjack's brought me on the right track - Callback was called twice. Thanks!

node.js - mean.io implicit/hidden parameters

i'm fairly new to node.js so this could potentially a total noob question. Anyway. I discovered the mean.io Project. In the official article-example on Github, there is the following method in the article-controller.
exports.update = function(req, res) {
var article = req.article;
article = _.extend(article, req.body);
article.save(function(err) {
if (err) {
return res.jsonp(500, {
error: 'Cannot update the article'
});
}
res.jsonp(article);
});
};
With a corresponding route
module.exports = function(Articles, app, auth) {
app.route('/articles')
.get(articles.all)
.post(auth.requiresLogin, articles.create);
app.route('/articles/:articleId')
.get(articles.show)
.put(auth.requiresLogin, hasAuthorization, articles.update)
.delete(auth.requiresLogin, hasAuthorization, articles.destroy);
// Finish with setting up the articleId param
app.param('articleId', articles.article);
};
So I'm confused. When and where does the route pass the req/res parameters to the articles.update, or any other articles function? Is there some hidden mechanism in node/express/mean I've missed out?
Thanks in advance.
app.route('/articles/:articleId')
.get(articles.show);
This means express will invoke articles.show method with request and response as first two parameters when a GET request comes with matching path
.

Resources