How to get Meteor.user() to return on the server side?
Asked Answered
C

4

37

in a file called /server/main.js (in order to ensure it is loaded last).

console.dir(Meteor.user());

Throws:

Error: Meteor.userId can only be invoked in method calls. Use this.userId in publish functions.

So I try to use, in the same file:

console.dir(this.userId);

returns:

undefined

so, not giving up, I'm thinking "that's fine I'll just read from the cookies in the header":

var connect = Npm.require('connect');

__meteor_bootstrap__.app.use(connect.query()).use(function(req, res, next) {
    console.dir(req.headers);
    next();
});

.... returns nothing in terms of cookies except for 'cookie: 'uvf=1''

I'm not sure what to conclude - this is senseless as I can otherwise use the Meteor.Account framework just fine, read/set user properties, etc. The server is clearly aware of the user, and the current user clearly logged in.

I'm at a complete loss, any explanation / hint / pointer would be greatly appreciated.

Combat answered 13/5, 2013 at 22:39 Comment(0)
R
53

You have to use Meteor.user() in a place where a request is made from the client (such as a Meteor.methods or a Meteor.publish).

It can't be placed anywhere else because meteor wouldn't know at that point in the code the user is supposed to bound to. If there is a place a request of some form is made from the client it can do this:

In a Meteor.publish:

Meteor.publish("collection", function() {
    //returns undefined if not logged in so check if logged in first
    if(this.userId) {
        var user = Meteor.users.findOne(this.userId);
        //var user is the same info as would be given in Meteor.user();
    }
});

In a Meteor.methods:

Meteor.methods({
    "test":function() {
        //should print the user details if logged in, undefined otherwise.
        console.log(Meteor.user());
    }
}

To use Meteor.user() on a server side route:

You need Meteor router installed as a package via meteorite to allow you to have a server rendered page. (installed via mrt install router)

A server side route could then handle the web request:

 Meteor.Router.add('/awebpage', function(id) {
     var userId = this.params.userid;
     var logintoken = this.params.logintoken;
     var isdirect = this.param.direct;
     var user = Meteor.users.findOne({_id:userId,"services.resume.loginTokens.token":logintoken});
     if(user) {
         //the user is successfully logged in

         return "You, "+user.profile.name+", are logged in!";
     }
     else
     {
         if(isdirect) {
             return "<h3>Loading</h3><script>window.location.href="/awebpage?direct=true&userid="+localStorage.getItem("Meteor.userId") +"&logintoken="+localStorage.getItem("Meteor.loginToken")</script>";
         }
         else
         {
             return "Not logged in"
         }
     }
 });

So now when you visit /awebpage it would check whether the user is logged in and do the thing you want when they are logged in. Initially there is a redirect to relay the data from localstorage back to the URI.

Rostand answered 14/5, 2013 at 0:14 Comment(8)
Thank you Akshat, here's the thing though, I'd like to use Meteor.user() to execute a conditional redirect to a different URL. So having to wait for the page to load, everything client and server side to be initialised, collections to be published, then make a method call from the client, wait for a reply and finally 'redirect' (which at that point would not be a 30x redirect anyway, because that ship has sailed), well, that doesn't work. If there's no way to do this I'm going to re-evaluate meteor, because right now I'm confused :(Combat
I think I do see what you want & it is very possible!. You need to use a server side router. Ill update the answer as soon as I get a shred of time, Meteor is awesome, if you want it to do something the old fashioned way it can do it without compromise pretty much all the time.Rostand
Thank you so much Akshat, I'm so happy to hear you know how to do this, that I've already marked you as the answer - looking forward to learn how to do this!Combat
I wouldn't have accepted it until you were happy with it. I've updated the answer with the new content, hopefully this was what you needed it to doRostand
Thank you Akshat, I'm happy with this solution, even though it's not server-side per say (ie it's not a header redirect). I've also seen it done using mini-pages, a similar solution until Meteor evolves a bit. Thank you again!Combat
Thanks - though in your snippet for Meteor.publish, I think you need to use findOne, ie. var user = Meteor.users.findOne(this.userId); //var user is the same info as would be given in Meteor.user();Zela
@RacingTadpole yes you're right. I think i had the intention of publishing the value which only works with Meteor.users.find() since the publish function needs to return a cursor. But Meteor.users.findOne(this.userId) is indeed what will be the same as Meteor.user()Rostand
"You have to use Meteor.user() in a place where a request is made from the client (such as a Meteor.methods or a Meteor.publish)." I can't seem to use Meteor.user() in Meteor.publish(). Getting the same error that OP got.Aplasia
J
3

You can expose the userId with Meteor.publish() to global scope. Then you can use it with Meteor.Router's server side routes.

--

/server/publications.js

CurrentUserId = null;
Meteor.publish(null, function() {
    CurrentUserId = this.userId;
});

-

/server/routes.js

Meteor.Router.add('/upload', 'POST', function() {
    if (!CurrentUserId)
        return [403, 'Forbidden'];

    // proceed with upload...
});
Jenellejenesia answered 23/7, 2013 at 17:23 Comment(3)
It will not allow you to do it as publish functions should only return a cursor or array of cursors.Overexcite
Only if your app is going to be used by only one user at a time... otherwise this approach will fail miserably, with users getting privileges to see other some random other user's things, etc.Rackley
this is aweful, since as @Rackley state will totally fail and impose security issues in an multiuser environment only the publish method is bound to a user the other context is out in the wild.Audwen
D
2

You can use the logged in callback

Accounts.onLogin((obj)->
  user = ob.user
)

Accounts.onLogin(function(obj){
  var user = ob.user
})
Diffluent answered 29/11, 2014 at 6:50 Comment(2)
Putting the user on the global namespace probably isn't best practices in 2015.Tress
Its coffeescript so its not. User would be var'dDiffluent
C
-1

I recently wrote a blog post describing solution to this: https://blog.hagmajer.com/server-side-routing-with-authentication-in-meteor-6625ed832a94.

You basically need to set up a server route using a https://atmospherejs.com/mhagmajer/server-router package and you can get current user with this.userId just like with Meteor methods.

Cantharides answered 17/5, 2017 at 23:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.