How to get the client IP inside a Route - meteor

I'm trying to retrieve the customer's IP address during Iron Router's routing process. I have a server-side function for this (getIP), however the "waitOn" function inside the route won't wait for the server function to return:
waitOn: function () {
Meteor.call('getIP', function(error, clientIp){...}}
Can I force it to wait, or can I get the IP in any other way?

According to the documentation, the waitOn hook must return a Handler, a function or an array. the reason why it's not working for you, is that Meteor.call on the client is always async, and you have to define a callback function, which is called when the method responds.
Given that nature, you could only use the Meteor method, if the waitOn code supported a Promise, that could be resolved on the method callback.
The only way I see this is the following:
use a Meteor.onConnection hook, and store the current IP Address of the user in the user's profile (Meteor.users collection)
setup a global Subscription that publishes the entire user profile (since by default Meteor.user only publishes a few default document fields).
on the route waitOn, query the Meteor.user collection, and you will see the current detected IP Address of that user
I hope this helps and that it works for you.

Related

FlowRouter, how to get data when routing depends on subscription

I'm writing a licence validation part for my application and want to redirect the user to a renewal page if and only if their licence has expired.
I am using FlowRouter and Blaze.
All my authenticated routes are in a group:
let authenticated = FlowRouter.group({
triggersEnter: [checkAuthenticated, checkSubscription]
});
I then check if the subscription is valid like so:
const checkSubscription = function(context){
let path = FlowRouter.current().path;
if (!Meteor.userId()){
return;
}
const sub = new Subscription();
if (sub.isInvalid() && path !=="/manage-practice/subscription"){
FlowRouter.go("/manage-practice/subscription");
}
};
My class subscription uses a collection that I can only load once a user has logged in. My problem is that the router usually triggers this redirection before this data has been loaded.
Is there a best practice approach to solve this?
Redirect with Triggers
I'm not sure about this being 'best practice' but one approach is to use the Flow Router redirect functionality on your login event.
You can see examples at: https://atmospherejs.com/kadira/flow-router#redirecting-with-triggers and https://github.com/meteor-useraccounts/flow-routing.
The initial login path (using Accounts.onLogin();) could be to a generic 'loading...' template while you evaluate the user's collection. On a callback you can then use the custom redirect function to either redirect to the requested page in your app, or redirect the user to your '/manage-practice/subscription' path.
FlowRouter.wait()
I have to confess I wasn't previously familiar with this second option, but I've just come across FlowRouter.wait(). This can be useful to delay the default routing process until some other evaluation is complete. I suspect this might only be relevant if a user logs directly into a page within your authenticated routing group.
Documentation: https://atmospherejs.com/kadira/flow-router#flowrouter-wait-and-flowrouter-initialize

Router.current().route.getName() is returning an error server side

Router.current().route.getName() is returning an error when I use it in a method call (server side method). I thought they say Iron-Router is supposed to work both client and server side. The error I get is
Error invoking Method 'mySeverSideMethod': Internal server error [500]
Please help.
You are half way right, the router works on both client and server. However the server-side implementation is meant for server side routes (eg. REST endpoints). There is no "state" sharing between client/server with iron:router (when invoked inside methods), so Router.current().route.getName() is going to throw you this error, because Router.current() is undefined.
Yes, iron:router can create server side routes, but that api is client only
From the docs:
Router.route('/download/:file', function () {
// NodeJS request object
var request = this.request;
// NodeJS response object
var response = this.response;
this.response.end('file download content\n');
}, {where: 'server'});
You have access to the NodeJS request object so you should be able to find what you need there, e.g. this.request.route, this.request.path.
When calling a Method, you're not going through a 'route' as defined by Iron-Router: it's a route defined by the Meteor framework. It does not care what route the client is on.
So, if you need to know from what page the client is calling the endpoint, you should pass it as a parameter to the Method.
Meteor.methods({
"myEndPoint": function(route) {
// use route here.
return //something
}
})

where sfGuard checks out perms and credentials in order to implement Google's Oauth 2

I want to integrate Google's Oauth2 in my symfony-1.4 CRM. I have successfully implemented this, I have extended sfGuardAuth in my own module, and now mysfGuardAuth is being used for siging and signout. Is there where I handle Google's Oauth2 with 2 extra actions:
executeCkeckGoogleAccess();
executeOauth();
The problem is to checkout if Google's token is still a valid one, I have to redirect in each action of everymodule to the action checkGoogleAccess in mysfGuardAuth module.
What I want is to check this in an implicit way in the same place where symfony, or sfGuard or whatever checks for the right perms or credentials before executing or not executing the requested action.
I only want to write the code once.
Thank you.
After some research this is how sfGuard checks everything.
When you make a request to a module action, before the action is executed, a new sfContext is dispached.
The sfContext gets the user that extends sfGuardUser and has some methods that are executed. There is where perms, session status and everithing else is checked
The user must be configured in apps/yourApp/lib
By default is apps/yourApp/lib/myUser which extends sfGuardUser. The most apropiate way to achieve this is to create a new user class like: apps/yourApp/lib/yourAppUser which extends aswell sfGuardUser, and there extend the methods initialize and/or shutdown with the functionality you want.
By this way I have achieved to get Google's Oauth2 working in my app.
I hope this is usefull for more people.
UPDATE
All described above is true, but if you want to check something always before an action execution you must use filters instead of whats described before.
Filters are executed before each action, so there you can checkout whatever you need having access to the current context, and set up new attributes for the user. In my case I wanna check if the requested action needs a google token, if true, then Another filter will check if the user has alraedy a valid token, in that case, nothing happens, otherwise, the user is redirected to the module/action which handles google token requests.
Comunication between diferent filters, actions and requests are handled via user attributes.
the user is an object of the clas myOwnUser which extends sfGuardSecurityUser, there the function signOut is extended in order to delete all attributes saved in "myOwnNamespace"

Meteor: Anyway to use Iron Router methods to call a route with post data?

Using iron router I can currently pass query parameter data to the route by doing something like:
Router.go Router.current().path.split("?")[0] + '?searchTerm=apple'
Which appends a searchTerm to the routes current path. Then in my router file I can access the search term with: this.params.searchTerm
But what if I want to send this data to the route in the body of the request? If I do not want to affect the URL then sending data to the route over the body would be useful. Just like a post ajax request? How can I do that with Router.go or anything else iron router supports?
Basically I want to get data to my route, but I dont want to use session, or affect the url in any way. So my last option is to pass the data in the body, but how?
Meteor doesn't work with old school ajax requests.
If you really must accept ajax requests you could (ab)use server-side routes in iron-router:
this.route('serverRoute', {
where: 'server',
action: function() {
this.response.end("THIS IS A SERVER ROUTE..");
}
})
But the accepted meteor way for handling what you described, would be to use Meteor methods on the server side define methods:
Meteor.methods({
foo: function (arg1, arg2) {
doStuff(arg1, arg2);
});
Then on the client you call them like so:
Meteor.call('foo', 1, 2, function (error, result) { /* CallbackHandleingCode */ } );
This does not affect the url whatsoever, as internally meteor uses websockets for exchanging data between client and server.

How can I hook into an authentication callback during the login process with Meteor?

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.

Resources