3

I am currently re-writing a PHP+Mongodb application in Meteor.

In the application, a session cookie that contains only a unique identifier is used. The server gets the browser's cookie and uses its value to load data from a collection. This is useful for knowing the client's current state. Using Meteor I need to be able to get the value of the browser cookie from the server code. How can I accomplish this?

In PHP, one might do it like so:

if(isset($_COOKIE["cookie_name"])) {
    //there is a browser cookie set with a name "cookie_name", 
    //and now I can act on that cookie's value, straight from the server
    echo $_COOKIE["cookie_name"];
}

I'm not sure if meteor's Session is what I'm looking for mostly because:

  • It doesn't seem to persist between page reloads (it creates a fresh session each reload)

  • There must be a way to disconnect the session by simply deleting the browser cookie

I'd like to handle this on the server because I want my sessions data to be private. Data about a session that isn't presented through a view (except for the session's unique identifier) must never be sent to the client.

George Brighton
  • 5,131
  • 9
  • 27
  • 36
Lawrence Weru
  • 188
  • 1
  • 8
  • This [SO Question](http://stackoverflow.com/questions/10326347/how-do-you-store-data-server-side-that-is-specific-to-a-client-in-meteor) looks like something I might be interested in.. I'll post back if my solution's there. – Lawrence Weru Nov 10 '13 at 03:14
  • nothing in the link I posted helped. The solution would have to be something that does not depend on a userid or a user being logged in. It looks like anything that uses/extends Meteor's sessions creates a new session on reload. – Lawrence Weru Nov 10 '13 at 04:09

3 Answers3

2

If I'm understanding correctly, you don't actually care about the cookie, you care about having user-specific data.

Comparison to PHP

Meteor clients communicate with the server via DDP which is an abstraction on top of http. Things like 'cookies' don't exist in the DDP level. Rather, you have access to powerful constructs like sync'd database collections and built-in remote procedure calls.

Meteor's Session object is a client-only concept that is designed for reactivity. It is not persisted between client visits and the server does not have access to it.

The rough equivalent to PHP's SESSION is a Meteor Collection, which is actually more durable than PHP's SESSION because it is persisted to the database.

User-specific data

Tracking user-specific data like you want in Meteor can be broken down into two parts:

  1. authenticated users
  2. anonymous users

Re: #1 - authenticated users

As @Tarang and @Cuberto have pointed out, the Meteor Accounts system (ex. accounts-password) has the concept of user-specific data built-in. It creates and manages the Meteor.users collection for you and provides the Meteor.user() function for getting an object specific to that user. It even has a built-in method for user-modifiable data in the profile field of the user object. The profile field is automatically published and is reactive as well (since Meteor.user() is reactive).

function doSomething () {
  var currentUser = Meteor.user(),
      profile;

  if (!currentUser) {
    // handle 'not authenticated' case
  } else {
    // already logged in
    profile = currentUser.profile || {name:'<not set>'};
    console.log('user ', profile.name, ' wants to doSomething');
  }
}

You can build your own authentication method but that seems like a recipe for disaster. Easier to write a script that converts from your existing DB structure to the Meteor Accounts structure and do it once in a big dump when you are ready to migrate your users over.

So the Meteor convention is:

  • User-specific data that the user should be able to modify goes in the user.profile field. Ex. user.profile.firstname, user.profile.lastname
  • User-specific data that is restricted should go on the root user object.
    Ex. The meteor-roles package stores user roles in a restricted, user.roles field.

Here are the relevant docs: http://docs.meteor.com/#meteor_user

Re: #2 - anonymous users

Meteor Accounts does not track anonymous users so you will need to track them yourself. You can use various methods to do this but the core is to store some identifying token on the client's machine in client code (either into localStorage or a cookie).

If you don't need to store user-specific data on the server and only want to change client-side stuff, such as what the users see, then you can do everything from the client.

If you need to store data on the server for anonymous users then you'll have to send the identifying token to the server along with each Meteor method call or database interaction (essentially what PHP does with the SESSION cookie). On the server, create a Collection called 'anonymousData' which will contain all of the user-specific info for your anonymous users, keyed by id token. The server-side functions can query that Collection with the id token the client passes to retrieve user-specific info for that user.

Keep in mind that if the user clears their cookies or deletes localStorage that data will be orphaned so some kind of a last-used check is important.

Community
  • 1
  • 1
alanning
  • 5,198
  • 2
  • 34
  • 33
  • Thanks! The PHP app already stores session data in MongoDB instead of using PHP's SESSION. In the app, multiple clients (or browsers) use the same user account. The clients are each interacting with the same data, but through different ways. ex: 5 office staff all share the same login. Once logged in, each client's state changes depending on its actions. The view that a logged-in client gets depends on the client's state. In this case I'd like to track the client's state, instead of the state of the user account. maybe I should treat each client as an 'anonymous user' (even when logged in)? – Lawrence Weru Nov 12 '13 at 01:37
  • A simple example is in the 'Leaderboard' demo. You can select different 'players' to use once you have loaded the app. And then you can take actions that pertain to the selected player. Different clients can use different players. However, when the browser refreshes, the client loses its state and must reselect a player. I'd need for the client's state to persist even after a page reload. Also the client's privileges depend on the client's state. Only the server should be able to give / revoke those privileges. Previously this was done by linking a cookie to 'session' data stored the database. – Lawrence Weru Nov 12 '13 at 01:53
  • Yeah, the fundamentals of your app don't seem like they would change much. You'd still track via cookie/localStorage. But your Meteor client could would do the interfacing and use the server-side as storage, passing the id token for reference. You probably don't even need server-side for this part; a deletion of cookies looses state anyways with no way to reconnect that user to their old state once they reconnect. So just do the state management and reactions client-side. – alanning Nov 13 '13 at 02:27
  • This was the most in-depth solution. – Lawrence Weru Dec 23 '13 at 06:22
0

You would have to parse the headers out. Look for a package called ip on atmosphere. This is trickier than it sounds though.

One thing you could do is instead of using cookie's use localStorage.

Try localStorage

localStorage.setItem("name", "value");

and to get a value:

localStorage.getItem("name");

Meteor already uses localStorage to store the user's logged in state & ID

Tarang
  • 75,157
  • 39
  • 215
  • 276
  • How can I access the localStorage item from the server? – Lawrence Weru Nov 10 '13 at 04:23
  • I believe you would need to pass it as a parameter with any server methods you call. – sbking Nov 10 '13 at 04:44
  • @LarryW. If you're accessing it from the server have you considered storing data for the logged in user instead? (If you are using the cookies for logged in users?) – Tarang Nov 10 '13 at 09:07
  • @Tarang, I'm not sure what you're asking. Data is stored for the client's session in a collection. When the app ran on Apache/PHP the browser cookie was used as the unique identifier in order to load that data, because PHP allowed the server to access the browser's cookie. In Meteor, I'm unable to access the browser's cookie from the server so I'm not sure how I can do this now. – Lawrence Weru Nov 10 '13 at 23:14
  • @Cuberto, the view that is presented to the client depends on the information stored in the cookie. The server doesn't know what views (& app privileges) to give the client without knowing the contents of the cookie. Could I send the cookie's information to the server before rendering anything on the client? I suppose this is the PHP way of doing things, so maybe there's a Meteor way of doing this? The reason I refrain from using Meteor's accounts-password package is because the app's database has a different user account structure, but maybe that will require a rewrite too. – Lawrence Weru Nov 10 '13 at 23:52
  • @LarryW. unfortunately I don't know how do this without using the Accounts package. I would advise looking at the source of the [accounts-base](https://github.com/meteor/meteor/tree/devel/packages/accounts-base) and [accounts-password](https://github.com/meteor/meteor/tree/devel/packages/accounts-password) packages to see how they achieve this through the `Meteor.user()` function. – sbking Nov 11 '13 at 02:50
  • @LarryW. another thing you should consider is that the "Meteor way" is thick clients. Server-only code is really meant for sensitive code. Remember that the client already has all the views, and does not need any permission from the server to display a view. If you're basing publish functions on the state of the client, what's stopping someone from simply editing that variable on the client and getting the newly published info from the server? I guess what I'm asking is, why exactly do you need the server to have access to this client state? If security is the #1 concern, change to Accounts. – sbking Nov 11 '13 at 02:57
  • @LarryW. I mean't to use users. You can store session data in the user's profile collection additionally If the user is logged in in can use `Meteor.user()` or `Meteor.userId()` in place of the cookie. If you already have a requirement the user needs to be logged in this shouldn't be difficult to migrate – Tarang Nov 11 '13 at 06:33
0

The cookie information is contained in the headers section of the response object of a HTTP call done withMeteor.http.call() called from the client using Meteor.call(). set-cookie is an array containing any cookies sent over from the server. Here's a screenshot of one of the results:

Athman
  • 584
  • 6
  • 15