Search code examples
javascriptnode.jsasynchronouscallbacknode.js-connect

cannot understand what callback does in a Connect module


I am reading a book about NodeJs Connect. There is this small part about basicAuth module. I know that basicAuth is now deprecated, but I cannot understand this simple code. The book says

Providing an asynchronous callback function

The final option is similar, except this time a callback is passed to basicAuth() with three arguments defined, which enables the use of asynchronous lookups. This is useful when authenticating from a file on disk, or when querying from a database.

Listing 7.7. A Connect basicAuth middleware component doing asynchronous lookups

enter image description here

And no other information. Thats the whole part about having a callback in the basicAuth

So, code gets the username and the password. Then hypothetical object User has a method authendicate that checks if this user actually exists. And when its finished, calls the gotUser function. gotUser contains either a returned error (=no user found with that username/password) or a returned user object (a user found with that username/password). Am I right?

gotUser checks if there is an error. If there is, returns and calls callback with an error argument. So wait, what will callback do at this point? Its not defined anywhere. Will it pass the error to an error handler function? And how?

If there is not an error, gotUser calls callback again with null(= no error) and user. Once again, what will callback do? Why pass the returned user to the callback and not grab its name, mail, age etc etc and use them on a session or fill the innerHTML of a tag or whatever?

Thanks


Solution

  • So wait, what will callback do at this point? Its not defined anywhere.

    The value of callback is defined by the basicAuth middleware.

    You can find its definition within the basic-auth-connect module, used by connect, in the module's index.js:

    callback(user, pass, function(err, user){
      if (err || !user)  return unauthorized(res, realm);
      req.user = req.remoteUser = user;
      next();
    });
    

    When gotUser() invokes callback(...), it's call the function(err, user){...} from the above snippet, passing the err and/or user along to be used.


    And, how they're used, in the two scenarios you were wondering about...

    gotUser checks if there is an error. If there is, returns and calls callback with an error argument. So wait, what will callback do at this point?

    If there is not an error, gotUser calls callback again with null(= no error) and user. Once again, what will callback do?

    The if (err || !user) condition will pass for both (one has an error, the other is lacking a user). It then considers the request unauthorized and will end the response immediately.

    function unauthorized(res, realm) {
      res.statusCode = 401;
      res.setHeader('WWW-Authenticate', 'Basic realm="' + realm + '"');
      res.end('Unauthorized');
    };
    

    Why pass the returned user to the callback and not grab its name, mail, age etc etc and use them on a session or fill the innerHTML of a tag or whatever?

    The middleware is applying separation of concerns, keeping itself as small and concise as possible. It's goal is just to determine a req.user and validate it.

    When it's done that successfully, other middleware in the application's queue will be able to reference the user that was found. This can includes using it to render markup from a view:

    // determine the user
    app.use(connect.basicAuth(...));
    
    // now make use of it
    app.use(function (req, res, next) {
      viewEngine.render('view', { user: req.user }, function (err, result) {
        if (err) return next(err);
    
        res.setHeader('Content-Type', 'text/html');
        res.end(result);
      });
    });
    

    Note: This is generalized and won't run as-is. You'll need to find and setup a view engine of your choice and substitute that into the snippet.


    Also, side note on...

    fill the innerHTML of a tag

    Though Node.js is executing JavaScript, it's doing so within its own environment, completely detached from any browsers. It's not possible to interact directly with the DOM currently seen by the user.