Search code examples
meteoriron-router

Managing login with meteor iron:router


I'm creating a private area in my meteor.js app; using iron-router, I have defined a controller, used by every private route:

panelController = RouteController.extend({
    waitOn: function() {
        Meteor.call('panelAuth', function(error, auth) {
            if (!auth) Router.go('/panel/login');
        });     
    }
});

This allows you to enter only if you are authorized server-side by the method 'panelAuth';

it works quite good but if you try to type in the address bar of the browser the url of a private route without being logged in (or logged without needed privileges) for a while you can see the route rendering, and then you are redirected to login page;

I would like to show a loadingTemplate while the 'panelAuth' decides if you can access, and avoid to show the private route for a while


Solution

  • As I'm sure you're aware, your invocation of Meteor.call is asynchronous. Your route is being rendered in the client, and then the asynchronous call returns, resulting in a re-route.

    Have you tried using onBeforeAction ? This is what it is for. Although I suspect you'll still have a problem if you insist on using an asynchronous method call.

    Router.onBeforeAction(function() {
      if (! Meteor.userId()) {
        this.render('login');
      } else {
        this.next();
      }
    });
    

    Since the client can see all of your templates (regardless of your security model), I'd focus attention on securing the publications. You could store a session variable on login indicating whether the user should be routed to the destination, and provide a more robust check on the data publication.

    Here's an example meteor app, using iron:router, accounts-password and accounts-ui.

    HTML:

    <head>
      <title>Login with Meteor</title>
    </head>
    
    <body>
    </body>
    
    <template name="hello">
      <h3>Hello</h3>
      {{> loginButtons}}
      <p><a href="{{pathFor 'blah'}}">Blah</a></p>
    </template>
    
    <template name="blah">
      <h1>BLAH!!!! YOU SAW ME!!!</h1>
    </template>
    
    <template name="login">
      username: <input type="text" name="username" id="username"/><br/>
      password <input type="password" name="password" id="password"/><br/>
      <button id="loginSubmit">Submit</button>
    </template>
    

    Javascript:

    // default page
    Router.route('/', function () {
      this.render('hello');
    });
    
    // secure page, requires login.
    Router.route('/blah', {
      onBeforeAction: function () {
        if (!Meteor.userId()) {
          this.render('login');
        } else {
          this.next();
        }
      }
    });
    
    // handle login, go to /blah if successful
    if (Meteor.isClient) {
      Template.login.events({
        'click #loginSubmit': function () {
          var username = $("#username").val();
          var password = $("#password").val();
          Meteor.loginWithPassword(username, password, function(error){
            if (!error) {
              Router.go('/blah');
            } else {
              alert("Errrrrrrrrrooooooorrrrr");
            }
          });
        }
      });
    }