10

I'm learning mean.io from this tutorial video, which shows the example package (created by mean package mymodule. It is also described under "Packages" on the docs). I would like help in understanding how the given authentication/authorization works.

The default sample package/module has a simple user authentication that on the client side

myapp/packages/mymodule/public/views/index.html contains:

    <li>
      <a href="mymodule/example/anyone">Server route that anyone can access</a>
    </li>
    <li>
      <a href="mymodule/example/auth">Server route that requires authentication</a>
    </li>
    <li>
      <a href="mymodule/example/admin">Server route that requires admin user</a>
    </li>

On the server side,

myapp/packages/mymodule/server/routes/mymodule.js, contains:

// The Package is past automatically as first parameter
module.exports = function(Mymodule, app, auth, database) {

  app.get('/mymodule/example/anyone', function(req, res, next) {
    res.send('Anyone can access this');
  });

  app.get('/mymodule/example/auth', auth.requiresLogin, function(req, res, next) {
    res.send('Only authenticated users can access this');
  });

  app.get('/mymodule/example/admin', auth.requiresAdmin, function(req, res, next) {
    res.send('Only users with Admin role can access this');
  });

  ...
};

The magic of the different authentication relies on the second argument of app.get() with additional authentication callback: none, auth.requiresLogin, or auth.requiresAdmin.

This is the authentication magic (also on github):

myapp/packages/access/server/config/authorization.js:

/**
 * Generic require login routing middleware
 */
exports.requiresLogin = function(req, res, next) {
  if (!req.isAuthenticated()) {
    return res.send(401, 'User is not authorized');
  }
  next();
};

/**
 * Generic require Admin routing middleware
 * Basic Role checking - future release with full permission system
 */
exports.requiresAdmin = function(req, res, next) {
  if (!req.isAuthenticated() || !req.user.hasRole('admin')) {
    return res.send(401, 'User is not authorized');
  }
  next();
};

QUESTION A: Why is it "exports.requiresLogin" and "exports.requiresAdmin" in the authorization.js instead of "somethingelse.requiresLogin" and "somethingelse.requiresAdmin"? Is this "exports" related to the myapp/packages/access/server/config/passport.js's exports: module.exports = function(passport) { ...}, github? If so, in what circumstances can we use this "exports"?


Since authentication's authorization rules is written up in package "access" and used in package "mymodule", Mean.io packages are not independent of each other. The Access package is registered on

myapp/packages/access/app.js, github:

var mean = require('meanio'),
  Module = mean.Module,
  passport = require('passport');

var Access = new Module('access');

Access.register(function(database) {

  // Register auth dependency

  var auth = require('./server/config/authorization');
  require('./server/config/passport')(passport);

  // This is for backwards compatibility
  mean.register('auth', function() {
    return auth;
  });

  mean.register('passport', function() {
    return passport;
  });

  Access.passport = passport;
  Access.middleware = auth;

  return Access;
});

QUESTION B: Does Mean.io automatically link all the packages or is there code to link packages somewhere? Is it linked due to the part with "This is for backwards compatibility" shown below? If so, where can "auth" be used? All the packages myapp/packages/? How about in the mean.io base app directory myapp/?

var auth = require('./server/config/authorization');

// This is for backwards compatibility
  mean.register('auth', function() {
    return auth;
  });

QUESTION C: Why is it "Access.passport = passport;", but "middleware" for "Access.middleware = auth;"? What what happen if it were "Access.auth = auth"?

Community
  • 1
  • 1
randwa1k
  • 1,502
  • 4
  • 19
  • 30
  • As far as `exports` go, that is part of Node's module-system. Whenever you `require` something you'll get the `exports` object from the file you required. – ivarni Aug 18 '14 at 06:12
  • For Question A, see http://stackoverflow.com/questions/5311334/what-is-the-purpose-of-node-js-module-exports-and-how-do-you-use-it – Steve Jansen Oct 22 '14 at 20:35

1 Answers1

1

REGARDING QUESTION A (on the use of exports)

In Node.js, assigning values to the exports object makes those values available to the code that requires the source file.

For example, given file foo.js:

exports.foo = "FOO";
exports.bar = "BAR";

and file main.js:

var foo = require('foo.js');
console.log('foo=',foo.foo,'; bar=',foo.bar);

running node main.js will output foo= FOO ; bar= BAR.

See, for example, Node's module documentation or this write-up on require and exports.

REGARDING QUESTION B (on package "linking")

The answer to this question is the complement to the answer to Question A.

There is code to "link" the packages. It is the require statement.

In your app.js source code, the first line (reading var mean = require('meanio')) will set the local variable mean to whatever values are assigned to exports object is when meanio.js and/or the meanio module is loaded.

Same with passport = require('passport'). In that case, the local variable passport will be equal to the value of exports after index.js in the passport module is loaded.

REGARDING QUESTION C

I'm not entirely sure what you are asking here, but let me take a stab at it.

In this case:

1) var mean = require('meanio') in line 1 "imports" the meanio module, such that the local variable mean is more or less set equal to the value of exports in the meanio module.

2) Module = mean.Module in line 2 sets the local variable Module equal to the value of mean.Module, which must have been assigned in the meanio module.

3) var Access = new Module('access') is instantiating an instance of the Module class, assigning it to the local variable Access.

4) Access.passport = passport assigns the instance variable named passport within the instance of meanio.Module named Access (to the value of the passport module required on line #3)

5) Access.middleware = auth assigns the instance variable named middleward within the instance of meanio.Module named Access (to the value returned by require('./server/config/authorization') in line 11).

I'm not familiar with the "meanio" module, but based on this code it looks like you are configuring the meanio.Module("access") instance (named Access) by assigning specific "magic" variable names.

In other words, rather than Access.passport = passport; Access.middleware = auth you might have Access.setPassport(passport); Access.setMiddleware(auth) or (rather than line 5) var Access = new Module('access',passport,auth).

That is, the author of the "meanio" module seems to have decided to use special variable names to configure the class rather than "setter" methods or parameters passed to the constructor. I assume that somewhere in the meanio code you'll find a reference to something like this.middleware and this.passport, where the code is assuming you have "filled in" those instance variables as happens in the last few lines in your code sample.

If you were to add Access.auth = auth then all that would happen is that the Access object would have a new attributed named auth whose value is equal to the that of the local variable auth.

If you used Access.auth instead of Access.middleware, I assume whatever code in the Access class that is using this.middleware will fail, since no value was ever assigned to Access.middleware and Access.auth is not one of the "magic" variable names that meanio is looking for.

Lela Jennings
  • 88
  • 1
  • 8