Session Token validation with NodeJS

#1

Creating a project using NodeJS with Express and parse-server as the backend. According to the Javascript Developer Guide, I can’t use Parse.User.current() or Parse.User.become() on the server side (seen here).

So on Sign Up/Login, I pass the Session Token to the client and store it in a cookie. Then pass the session token back on every request. I do not see a method that lets me verify the Session Token is still valid.

Am I missing something obvious or do I just need to run a query every time to validate the token?

#2

can you paste an example? is this cloud code?

if it’s in a cloud function, you can get the user from request.user .

what you’re doing doesn’t sound right to me, so let’s have some more info about what you’re trying to do with this backend code.

#3

No it’s not cloud code, just the Javascript SDK.

Signup Code (Server side with NodeJS/Express using post route):

  const user = new Parse.User();
  user.set('username', data.username);
  user.set('email', data.email);
  user.set('password', data.password);

  try {
    await user.signUp();
    res.cookie('token', user.getSessionToken());
    res.sendStatus(200);
  } catch (error) {
    console.log("Error: " + error.code + " " + error.message);
    res.sendStatus(400);
  }

This is about as far as I have made it as I have no idea how to validate that token on the next request.

My thought is, the client will send the token along with every request, validate that token against the “Session” class, get the User associated with it and then go from there.

But this seems quite complicated which makes me think there must be a better way.

#4

Interesting. I think you’re on the right path actually. Just use the session token. The query will fail if it’s not vailid with a parse error that’ll be clear.

1 Like
#5

Well that’s reassuring, thank you!

Although, what I’m doing seems like it should be part of the SDK. Something like Parse.Session.Validate(token) would return a bool or maybe the User associated. Perhaps I will look into creating a PR for this once I get it implemented.

Just wondering how other people using NodeJS and Parse-Server have solved this problem. Haven’t found any examples fully implementing the Login/Sign up functionality with Node.

#6

This may be unrelated, but is there a reason that you do pass the session token back to the user and manually store it in a cookie?

On the client side you can use Parse.User,.current(), that’s perfectly acceptable. You can use signUp on the client side, rather than the server side, but do any validation or processing in a beforeSave handler on the user class.

#7

There are two reasons I’m trying to avoid using a Parse SDK on the client side:

  1. The project is split into two parts, a UI/UX which controls the client side, then an API/Backend which is obviously the server side. I’d like to keep them as separate as possible.
  2. I try to keep the scripts loaded on the client side as minimal as possible to boost load time.

But if the best way to handle use Signup/Login with Parse is on the client side, I’m willing to listen.

#8

@jrs40492, why don’t you do this on the cloud code or don’t you have access? I don’t use any client side Parse SDK, purely use the REST api. In cloud code I use these functions:

User.LoginUser = function (req, res) {
  let userQuery = new Parse.Query(Parse.User);
  Parse.User.logIn(req.params.username, req.params.password, { installationId: req.params.installationId }).then(
    user => {
      if (user) {
        // an annoying additional query in order to get the Pointer attributes for a paid customer.
        userQuery
          .first({ sessionToken: user.getSessionToken() })
          .then(
            freshUser => {
              const userObject = mapUserObject(freshUser);
              // the above request is stripping out the sessionToken and I
              // don't know why, it's the same as refresh user below! We'll have
              // to add it in or we get a ton of errors
              userObject.sessionToken = user.getSessionToken();
              res.success(userObject);
            },
            error => {
              res.error(error);
            }
          )
          .catch(error => res.error(error));
      }
    },
    error => {
      res.error(error);
    }
  );
};

function mapUserObject(user) {
  var userObject = {
    id: user.id,
    sessionToken: user.get("sessionToken"),
    username: user.get("username"),
    firstName: user.get("firstName"),
    lastName: user.get("lastName"),
    email: user.get("email"),
    emailVerified: user.get("emailVerified"),
    createdAt: user.createdAt,
    updatedAt: user.updatedAt
  };

  return userObject;
}

If you can’t do this directly with the server then you can maybe call this from the Node intermediate server you seem to be running and return the user object to the client. Notice the returned user object so it can be held in locally. Sorry it’s messy I don’t have much time to respond just now. I will take a look back at this thread a little later to see if I can help anymore.

#9

I just started using Parse, so I haven’t really explored Cloud Code yet. Perhaps I’m missing something but what is the benefit to using Cloud Code over putting the same logic into my API project?

Also, my main issue is every request after the Login. Once the user is verified, I’m trying to find the best way to validate the following requests.

Edit: Just attempted to try my idea above (Query the Session class to get pointer to the User) however, this seems to not be allowed? Gives me an “Invalid session token” error and a quick Google says it’s not allowed due to security reasons.

#10

For me, and what I think is quite normal is for the node server running parse to actually be your API, this additional intermediate layer is an addition complication that’s not required. You can just do everything on the parse server.

If done this way you can easily access the parse user via request.user.getSessionToken(). This session token is passed to your query or save requests to authenticate those calls.

Happy to answer other specifics if you want.

#11

Sorry, I haven’t had time to work on this project the past couple of weeks.

@Simon I’m going to attempt your idea now and see if it works. From what I remember, I didn’t have access to the user object in the request but that may be something I’m doing wrong.

Do you have any code samples by chance?

Edit: Tried using req.user and it is undefined when it comes from the client.

#12

Anyone have any more thoughts on this?

I’ve tried Googling but I can’t find a single implementation of Parse + Node.js that demonstrates user validation after they log in. Every tutorial stops at the login part which I have implemented and is working fine.

Just seems insane that I can’t find any info on this. Either no one uses Parse + Node.js or I’m missing something so obvious that no one has ever asked this question before.

#13

Hi @jrs40492,

Sorry for the delay.

On the client side of the application you are going to have to package up the REST call to include the relevant information, namely the sessionToken.

Take a look at this SO question I asked about 3 years ago. It’s an Angular question, however you should be able to see what is happening with the REST call data.

Essentially ensure you are passing the session token

const headers = {
  'X-Parse-Application-Id': PARSE_APPLICATION_ID,
  'X-Parse-Session-Token': sessionToken;
  'Content-Type': 'application/json'
};

Following login you are going to have to store the session token on the client side in order to pass it with subsequent API calls.

In your example, if you are getting an undefined on the user you need to ensure that they are already logged in and you’re passing the session token. Looking at the example above I am actually doing a cheeky additional query in order to get the full user in the login function as normally the login only returns the session token rather than the full user object.

Post some code if you’re still struggling.

#14

I see that what I’m trying to do is possible by using the REST API for Parse. I will try implementing this and see if it works.

What I don’t understand is, why can’t I just use the Javascript SDK to do the same thing? If it’s possible, there is 0 documentation on it in the developers guide or API.

#15

The methods I am showing you mean that no Client Side Parse Library is needed. However, if you wanted to use a client side Lib then you have 2 choices.

  1. Use the normal login method and you will then have the session token stored in local storage and the Parse JS lib will add this to all requests to the server.

  2. You can use the Parse lib to run custom JS functions in the cloud by running

const params =  { movie: "The Matrix" };
const ratings = await Parse.Cloud.run("averageStars", params);
// ratings should be 4.5
});

See here for the JS calling of cloud functions

#16

Method 1 is what I’m trying to do. But the issue I’m having is, once I have the session token, I can’t figure out how to get the user associated with that token.

I know I could store their User ID in a cookie as well and just use both the Session Token and User ID in each query but I prefer exposing as little as possible to the client side.

#17

If you want to expose as little as possible in the client side then I advise not using the Client JS library and just use the REST API.

But anyway, if you have a session token then you have everything you need on the Cloud. Passing the session token in the method I have show above will give you the request.user object in the cloud code. Unless you have a reason to store the userId on the client side then it’s not needed to make server calls as a logged in user, so long as the session token is valid.

#18

I’m only using the Javascript SDK library on the server side in Node. The only thing that’s ever passed to the client is their token upon login/sign up.

I’ve made some progress using the REST API. Using Postman with the exact same values, I get a valid response, however, I’m using axios in Node.js and it’s giving me “Error: unauthorized”. Here is my code:

const headers = { 'X-Parse-Application-Id': process.env.APP_ID, 'X-Parse-REST-API-Key': process.env.API_KEY, 'X-Parse-Session-Token': req.cookies.token };

axios
  .get(`${process.env.SERVER_URL}/users/me`, headers)
  .then(data => {`
#19

Hang on…is this your stack?

Parse Server 
NodeJS Server (Running the Parse JS SDK?) 
Client Side

I’m pretty sure that the Parse JS SDK is for the client side. It needs to be run in the browser as it stores a load of info into the browsers local storage. I might be wrong, but I’ve never even given it a thought that the ParseJS module could be running on NodeJS.

Maybe someone else can help with that as I’ve never seen that setup before.

Personally I don’t see why there is a Node JS server in the middle of this, can’t you just do everything on the Parse server as it’s just a Node Server it’s self.

To go Client > Node Server > Parse Server > Node Server > Client is a really long way around.

#20

Well right now, nothing is technically split out. Everything is contained in one NodeJS application. The current flow is:

  1. Start NodeJS server
  2. JS SDK initializes Parse Server
  3. Application starts running on port X
  4. User logs in, response contains session token stored in cookie
  5. User navigates to restricted page, passes the session token to GET request
  6. NodeJS Server (using Express Router) receives GET and session token
  7. I validate the session token server side then send the response (this is where I’m stuck)