0

I am working on an express-based nodejs application. I need to authenticate users for login with the help of a third-party site. Overall, theres a client (written by me), a server (written by me, in nodejs), and this third-party API that I must use for validating users.

The steps are roughly:

  • The client sends the server a request with a ticket (hash)
  • If the user is not already logged in, the server sends q request to the third-party API to validate the user
  • If the third-party site says the login is OK, return a NEW_LOGIN_ACCEPT reply to the client
  • Else, return FAIL to the client

I am having trouble figuring out how to properly structure this with nodejs. I need to wait on sending the response to the client until the request to the third-party API has completed. My understanding is that blocking requests is not the "right" way to do things in node, and instead I should utilize callbacks. I cannot get the callbacks to work right.

The server-side code for handling this request is below, with some slight modifications made:

app.get('/login', function (req, res, next) {
  ticket = req.query.ticket;
  if (! has_valid_session(ticket)) {
    request(third_party_api_url + '&ticket=' + ticket,
      function (error, response, body) {
        if (body.indexOf('authenticationSuccess') != -1) {
          add_session('example_username', 'example_id');
          res.send('NEW_LOGIN_ACCEPT');
        } else {
          res.send('FAIL');
        }
      });
  } else {
    res.send('SESSION_OK');
    session_activity(username);
  }
  next();
});

The problem is that the response (res) has already been sent back to the client by the time the callback is hit. The error I get is:

_http_outgoing.js:500
    throw new errors.Error('ERR_HTTP_HEADERS_SENT', 'set');
    ^

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

I realize the "node-ic" way of doing things is via promises/callbacks, but I cant figure out how to keep the response (res) alive through a chain of callbacks.

I looked at the answer to this question: Wait for request to finish Node.js which seems like a similar thing, but I'm not sure if/how that would work for me.

bddicken
  • 1,412
  • 1
  • 15
  • 16
  • Callbacks or promises are going to be the way to go. Personally I prefer promises but callbacks can be easier to wrap your head around. I'd say try writing some code that uses callbacks and post it in your question so we can see what you've tried. – bmceldowney Feb 27 '18 at 00:58
  • 1
    You need to show us what you've tried or it's impossible to properly answer the question. As you mentioned, if this is all the question you have then the answer you posted would be the answer – slebetman Feb 27 '18 at 01:07
  • 2
    We aren't very useful with theoretical questions. We are very useful when you show us your actual code and we can then show you exactly how to fix it or make it work. Currently, this is a theoretical question that asks for a broad tutorial on how to program asynchronous development. There are several thousand answers on that general topic here. If you're not going to give us any code specifics, then the best we can say is to go read some of those thousands of other answers or tutorials on the web. – jfriend00 Feb 27 '18 at 01:10
  • show what you have done that doesnt work and people will help you figure out issues. – Jiby Jose Feb 27 '18 at 01:43
  • 1
    I have read several other related questions, but I have not been able to determine a solution from them, which is why I tried to ask the question in a little more a generic "what the best technique" kind of way, rather than "please help me solve a problem for a specific piece of code". But I can share some code . . . let me update my question. – bddicken Feb 27 '18 at 18:14
  • 1
    It looks like your only mistake is the call to `next()`. Just remove that and the `, next` from the parameter list, pretty sure that should fix your problem. – Patrick Roberts Feb 27 '18 at 18:21
  • @PatrickRoberts that worked! Would you mind elaborating as to why removing that call does the trick? – bddicken Feb 27 '18 at 22:24
  • 1
    Because calling `next()` passes the request on to the next candidate route, which in your case appears to be responding synchronously, before your asynchronous 3rd party request has a chance to complete, thus you attempt to send a response twice. – Patrick Roberts Feb 27 '18 at 22:32

0 Answers0