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
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");
}
});
}
});
}