Im using meteor + Iron Router and I would like to handle (in the onbefore web hook) the case where meteor auto refreshes all connected clients and redirect to the home route ('/').
Is there a flag to determine when a refresh is caused by meteor live updating vs a client triggered refresh?
An auto-refresh triggered by a code change leaves Session variable values in-tact, whereas a client-triggered refresh will reset them all to null. So, if I understand you correctly, you could check for the presence of a Session variable in the Meteor.startup callback on the client and call Route.go('/') if it's null. Example:
if (Meteor.isClient) {
Meteor.startup(function() {
if (!Session.get('keyKnownToHaveValue')) {
Route.go('/');
}
});
}
Related
I am using Meteor with Iron Router and Stripe. Everything is working great, but I cant figure out how to re-direct user to a final order complete page after the Stripe charge is completed.
On my client side I have a modal box that appears which contains a button that says, "Pay" When the Pay button is clicked an event is fired that calls up and opens Stripe Checkout.
The Stripe Checkout then initiates on the client and the user is able to enter the card details and submit the payment. The server side method for charging the card thru Stripe is completed and I also have some other basic database tasks that are being performed to log the result and complete the order status.
I have created a route using Iron router that I want the user to be re-directed too after the Stripe Payment is completed.
As of now the modal box continues to stay on the screen. I am trying to make the Router.go send user to the order page that had been setup after the order is finished.
I beleive the Iron Router Router.go is used client-side only. How can I complete the order process and make the client-side modal box disappear after the Stripe charge is completed and re-direct user to a final complete page.
When I use the Router.go on server-side I am getting error:
Exception in callback of async function: TypeError: Object function router(req, res, next) {//XXX this assumes no other routers on the parent stack which we should probably fix
You can't force the user's browser away from it's page if you don't already have some logic on the page to "remote control it". Fortunately there are a number of options for the latter:
If you are using a router you could make the route itself reactive, depending on the content of a subscribed collection. Alternatively, you can have an Tracker.autorun or an observeChanges block on your client side code that checks for changes to that "control" collection and then execute Router.go accordingly.
As for the control collection, a simple collection like:
var Control = new Mongo.Collection('control');
would do, and then you insert into it from the server when the event occurs (Control.insert({route: "newroute"})), and check for content changes on the client, for instance like so:
Control.find().observeChanges({
added: function(id, doc) {
Router.go(doc.route);
});
got quite a complicated problem.
In my application, there is a 5 step user registration/setup process.
Create Account
Choose an option
Choose more options
Create subscription with Stripe
Connect Stripe account so application has read-only access to user's Stripe data
After these steps, the user has access to the dashboard.
What I want to do is make sure that
If a user leaves/disconnects during a step when they log back in they will be redirected to the step they hadn't completed (currently my login submit handler redirects straight to dashboard, so I'm not sure how to handle this)
Once a user has completed all steps if they were to navigate to the route of a step they won't be able to, it will just forward to dashboard
I'd really appreciate any help on how to do this. So far what I've done is create a field in the database attached to the user document which records what step they have completed last, and if they have completed all it stored "dashboard".
I'm using Iron Router and I think the answer may lie in onBeforeAction hooks but I'm not 100% sure.
Any help appreciated!
I actually managed to work this out so thought I'd post for anyone who has a similar issue.
I extended the route controller for the routes I want to protect. The extended controller calls a meteor method that returns the value in the database that is stored when a user completes a registration step. I stored this value as a string which is the route the user should be redirected to.
So now, when a user signs up and completes step 2 then logs out a few things happen.
The app knows they have completed step 2 so stores the route for step 3 in a field on the user document (eg. Meteor.user().loginRedirectTo = '/subscribe')
User comes back and logs in, the loginhandler has a onBeforeAction that calls the meteor method which returns the value from the database to the router of where the user should go next.
Router sends users to that page.
Here's the code for it
Extended Controller:
StageController = RouteController.extend({
onBeforeAction: function() {
Meteor.call('getUserRedirect', function(error, route) {
if(error) {
console.log(error);
} else {
Router.go(route);
}
});
this.next();
}
});
Meteor method to return route user needs to go to:
Meteor.methods({
'getUserRedirect': function () {
if(!Meteor.userId()) {
throw new Meteor.Error(401, "You aren't even logged in, stop calling methods!");
}
var user = Meteor.user();
var redirectRoute = Meteor.user().loginRedirectTo;
return redirectRoute;
}
});
And finally assign that controller to each of the routes with this code:
Router.route('/dashboard', {
template: "dashboard",
name: "dashboard",
controller: "StageController"
});
I have an issue when using Meteor and trying to reload current page (I use Iron Router for routing). When I try to reload page using:
window.location.reload();
// or
window.location = window.location;
The reload is happening but soon after (only in production, not at localhost) user gets logout. I think this is a bug. Is there any way to reload page without logout?
Or, maybe, there is a bug in my controllers in routing. There are configured as follows:
RouteController.extend({
onBeforeAction: function () {
if(Meteor.user() !== null) {
if(Roles.userIsInRole(Meteor.userId(), 'client'))
this.next();
else
Meteor.logout();
} else
this.redirect('login');
}
})
Are there correct? Maybe when browser try to render new page the iron router calls Meteor.logout() due to some bug in here.
Well, you are logging him out yourself ;-)
This, as so often, seems to be yet another instance of data synchronization delay. I bet you that the issue is that the data underlying Roles did not yet sync the first time when the following code gets executed:
if(Roles.userIsInRole(Meteor.userId(), 'client'))
this.next();
else
Meteor.logout();
Hence you are logging the user out yourself. The data will arrive on the client shortly after, and the same block will probably execute reactively again, but that's too late.
You will need to find a way to wait for the data underlying Roles to be ready before executing this block. Since you are using iron router you can just add the appropriate waitOn subscription to the route (or the route controller if you are using it globally). Which collection to wait on, I don't know, since I don't know what's behind Roles (it's not a feature of iron router).
I implemented my own login system, because I'm using a third party web service to authenticate users against an enterprise authentication system. As such, I built a form that calls a server method to make the web service call to the auth system, and if the credentials are valid, it sets a session variable with the user's id. This is how I change the template to show the main screen of the application and not the login screen. Works fine. And the logout button then just sets that userid session variable to false, effectively hiding the main application screen and showing the login form again.
<body>
{{#if loggedInUser}}
{{> navbar}}
{{> mainScreen}}
{{else}}
{{> customLogin}}
{{/if}}
</body>
Template.navbar.helpers({
loggedInUser: function () {
return Session.get('userName');
}
});
'click #logoutButton': function () {
Session.set("userName", false);
}
What I have discovered though, is that the local minimongo collections/subscriptions are still in the browser, and accessible in the console, after the user logs out.
I did some searching but didn't find concrete solutions as to how to properly clear out (or stop?) these subscriptions on the client. In fact, the top 3 hits on a search for "meteor publish subscribe " don't mention stopping or security upon logout.
One suggestion on SO was to save the subscription handle ... but I'm calling subscribe multiple times, so it seems I would have to store up an array depending on how many different subscribes the user triggered during their use of the application, and then go through them calling "stop" on each handle when logging out??
I'm hoping there's a simple way to stop all subscriptions... seems like a logical thing to do for security when a user clicks a logout button.
Thanks!
Could you not use .stop() function on the collection?
var subscription = Meteor.subscribe("info");
//on logout
subscription.stop();
According to the docs:
stop()
Cancel the subscription. This will typically result in the server directing the client to remove the subscription's data from the client's cache.
Updated: Maybe check out this package: Subs Manager. It appears they may be able to do what you want, specifically from their readme:
Clear Subscriptions
In somecases, we need to clear the all the subscriptions we cache. So, this is how we can do it.
var subs = new SubsManager();
// later in some other place
subs.clear();
Using meteor, I'd like to be able to augment a user record after they've logged in (authenticated) with an external service to get their authorization claims.
Update
I am using the {{loginButtons}} handlebars helper widget.
Currently, I see an Accounts.validateNewUser and an Accounts.onCreateUser that can be hooked into during the creation of a new user. These would be helpful initially, but my need is recurrent.
I understand that there is the allow function that hangs off the Meteor.Collection as a means of authorizing a user's access to the collection -- which is precisely where I would use the claims that I intend to augment the user with to determine authorization.
Does anyone know of a hook during the login process that would allow me to do this?
The easiest way to get around the lack of a callback is to use the relevant reactive variable:
Tracker.autorun(function() {
if (Meteor.userId()) {
// do something when they've just logged in.
}
});
The context setup by autorun will only re-run when the value of Meteor.userId() changes -- i.e. when they login.
Starting with Meteor 0.7.2 version, there's a server side hook available :
Accounts.onLogin()
From the Meteor docs on login with password there appears to be a callback already in place for what you need to do:
Meteor.loginWithPassword(user, password, [callback])
callback Function
Optional callback. Called with no arguments on
success, or with a single Error argument on failure.
The login callback is supported for both Meteor and external authentication services.
#Makita, thank you for the answer. I did see that callback param but what I failed to mention in my question was that I had no low-level hook to it because I am using the {{loginButtons}} handlebar helper to inject the user management widget (which is way awesome).
The problem with this approach was that I did not have access to a callback after authentication happened, so I created this pull request which I hope will be merged to solve the issue:
https://github.com/meteor/meteor/pull/479
With this, you should be able to call:
Accounts.ui.config({
onSuccess: function (err) {
//perform addl authorization on Meteor.user() here
}
});
For people coming to this in 2014, You can use the onLogin callback server side
Accounts.onLogin((obj)->
user = ob.user
)
For the accepted answer, there was an issue on page reload that messed up that solution. I ended up doing something like this (Its using angular meteor but you should get the gist, just substitute autorun for Tracker)
.run(function($meteor,$rootScope,$state,$localstorage){
$meteor.autorun($rootScope, function(){
var id = Meteor.userId();
if(id == undefined || id == null){
id = '';
}
if($localstorage.get('user_id','') != id){
$localstorage.set('user_id',id);
if(Meteor.userId()){
//On login
$state.go('profile',{user_id: Meteor.userId()});
}else{
//On logout
$state.go('main');
}
}
});
});
I only recommend this solution for development, when I stop using the default accounts-ui I'm going to have to implement the lower level functions and there will be no need for this.