How to get the user IP address in Meteor server?
Asked Answered
C

6

27

I would like to get the user IP address in my meteor application, on the server side, so that I can log the IP address with a bunch of things (for example: non-registered users subscribing to a mailing list, or just doing anything important).

I know that the IP address 'seen' by the server can be different than the real source address when there are reverse proxies involved. In such situations, X-Forwarded-For header should be parsed to get the real public IP address of the user. Note that parsing X-Forwarded-For should not be automatic (see http://www.openinfo.co.uk/apache/index.html for a discussion of potential security issues).

External reference: This question came up on the meteor-talk mailing list in august 2012 (no solution offered).

Condorcet answered 12/2, 2013 at 22:41 Comment(1)
The canonical answer is to use the user-status package.Harlot
C
34

1 - Without a http request, in the functions you should be able to get the clientIP with:

clientIP = this.connection.clientAddress;
//EX: you declare a submitForm function with Meteor.methods and 
//you call it from the client with Meteor.call().
//In submitForm function you will have access to the client address as above

2 - With a http request and using iron-router and its Router.map function:

In the action function of the targeted route use:

clientIp = this.request.connection.remoteAddress;

3 - using Meteor.onConnection function:

Meteor.onConnection(function(conn) {
    console.log(conn.clientAddress);
});
Catanzaro answered 26/3, 2014 at 10:2 Comment(8)
Looks new and exciting. Do you know when this was added to Meteor?Condorcet
Don't know when the first one was added. Unfortunately my second example is using express, but it can be done similarly with iron-router and in the action function you can use this.connection.clientAddress or this.request.connection.clientAddress; I'll update my post as soon as possible.Catanzaro
I edited my response and replaced the expressjs solution with the one using the newest router for meteor.Catanzaro
Solution #1 is exactly what I was looking for. Thank you!Condorcet
Keep in mind that these will not work if you are behind a proxy. In that case you can try something like this.connection.httpHeaders['x-real-ip'] or whatever headers your proxy adds to your packets. This works for me behind nginxFungicide
Sarfata: I think Client Info was added around Jan 2014. (Hat tip to @Andrew-Mao's answer).Harlot
Sarfata: I think Client Info was added around Jan 2014. (Hat tip to @Andrew-Mao's answer). The value is now built in core - see clientAddress at docs.meteor.com/#/full/meteor_onconnectionHarlot
I'm running behind an nginx proxy. I had to set proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; in my nginx config under location / { and also process.env.HTTP_FORWARDED_COUNT = 2; as a Meteor environment value, instead of 1 as stated in the Meteor docs. I also had some luck with this Meteor.onConnection(function(conn) { var forwardedFor = conn.httpHeaders['x-forwarded-for'].split(","); console.log(forwardedFor[0]); });Tindle
F
2

Similar to the TimDog answer but works with newer versions of Meteor:

var Fiber = Npm.require('fibers');

__meteor_bootstrap__.app
  .use(function(req, res, next) {
    Fiber(function () {
      console.info(req.connection.remoteAddress);
      next();
    }).run();
  });

This needs to be in your top-level server code (not in Meteor.startup)

Felspar answered 13/6, 2013 at 3:7 Comment(4)
How do you link that to a Meteor.user ?Guest
Thanks for the contribution. It gets an IP address but I always get 127.0.0.1 - I think this is because Meteor starts a first instance with an http proxy that forwards request to the second instance. The incoming request is always going to be 127.0.0.1.Condorcet
Luckily for us - the http proxy forwards the incoming ip so we can do something like: console.info(req.headers['x-forwarded-for']); - However, I still cant link that to the current user because the Meteor.userId() can not be called directly (Error: Meteor.userId can only be invoked in method calls. Use this.userId in publish functions.). Any ideas?Condorcet
Has anyone been able to figure out a version of this that works? I always get 127.0.0.1 as a response. I'm resorting to a JSONP request to a service like ipinfo.io in the client, which is definitely not ideal.Isar
B
2

This answer https://mcmap.net/q/509079/-how-to-get-the-user-ip-address-in-meteor-server already does a good job on showing how to get the client IP address.

I just want to note that if your app is served behind proxy servers (usually happens), you will need to set the HTTP_FORWARDED_COUNT environment variable to the number of proxies you are using.

Ref: https://docs.meteor.com/api/connections.html#Meteor-onConnection

Bailee answered 4/12, 2016 at 3:7 Comment(0)
S
1

You could do this in your server code:

Meteor.userIPMap = [];
__meteor_bootstrap__.app.on("request", function(req, res) {
    var uid = Meteor.userId();
    if (!uid) uid = "anonymous";
    if (!_.any(Meteor.userIPMap, function(m) { m.userid === uid; })) {
        Meteor.userIPMap.push({userid: uid, ip: req.connection.remoteAddress });
    }
});

You'll then have a Meteor.userIPMap with a map of userids to ip addresses (to accommodate the x-forwarded-for header, use this function inside the above).

Three notes: (1) this will fire whenever there is a request in your app, so I'm not sure what kind of performance hit this will cause; (2) the __meteor_bootstrap__ object is going away soon I think with a forthcoming revamped package system; and (3) the anonymous user needs better handling here..you'll need a way to attach an anonymous user to an IP by a unique, persistent constraint in their request object.

Sear answered 13/2, 2013 at 1:2 Comment(2)
Looks like a very easy and cool solution: I thought I was going to write an atmosphere package just for that. Thanks a bunch, I will test soon!Condorcet
Unfortunately, it does not work (meteor 0.5.9): "Error: Meteor code must always run within a Fiber" - The problem is in Meteor.userId(). - That's a bummer, I really liked your idea.Condorcet
C
1

You have to hook into the server sessions and grab the ip of the current user:

Meteor.userIP = function(uid) {
      var k, ret, s, ss, _ref, _ref1, _ref2, _ref3;
      ret = {};
      if (uid != null) {
            _ref = Meteor.default_server.sessions;
            for (k in _ref) {
                  ss = _ref[k];
                  if (ss.userId === uid) {
                        s = ss;
                  }
            }
            if (s) {
                  ret.forwardedFor = ( _ref1 = s.socket) != null ? 
                        ( _ref2 = _ref1.headers) != null ? 
                        _ref2['x-forwarded-for'] : void 0 : void 0;
                  ret.remoteAddress = ( _ref3 = s.socket) != null ? 
                        _ref3.remoteAddress : void 0;
            }
      }
      return ret.forwardedFor ? ret.forwardedFor : ret.remoteAddress;
};

Of course you will need the current user to be logged in. If you need it for anonymous users as well follow this post I wrote.

P.S. I know it's an old thread but it lacked a full answer or had code that no longer works.

Conversational answered 19/8, 2013 at 13:17 Comment(1)
Very sorry I did not consider or reply to your post earlier. The link you shared is a 404 now. Could you update it? If it works for anonymous users too I would be happy to confirm your answers as the right one!Condorcet
L
1

Here's a way that has worked for me to get a client's IP address from anywhere on the server, without using additional packages. Working in Meteor 0.7 and should work in earlier versions as well.

On the client, get the socket URL (unique) and send it to the server. You can view the socket URL in the web console (under Network in Chrome and Safari).

socket_url = Meteor.default_connection._stream.socket._transport.url
Meteor.call('clientIP', socket_url)

Then, on the server, use the client's socket URL to find their IP in Meteor.server.sessions.

sr = socket_url.split('/')
socket_path = "/"+sr[sr.length-4]+"/"+sr[sr.length-3]+"/"+sr[sr.length-2]+"/"+sr[sr.length-1]
_.each(_.values(Meteor.server.sessions), (session) ->
    if session.socket.url == socket_path
        user_ip = session.socket.remoteAddress
)

user_ip now contains the connected client's IP address.

Labile answered 3/3, 2014 at 18:50 Comment(2)
That works but relying on the client to find the IP is not good enough when you are trying to use the IP address for security purposes. It's too easy to fake. The user could just open the console and call Meteor.call('whatever IP').Condorcet
Well it's not so easy to fake because you're going to check Meteor.server.sessions for the correct SockJS URL sent to you by the client. (SockJS URLs are unique, e.g. /sockjs/580/uqafmf0o). If you don't find the URL sent to you by the client, you know that they're faking it, so you return an error. Not sure what you mean by your comment above, but the client doesn't send the server their IP in the code above.Labile

© 2022 - 2024 — McMap. All rights reserved.