Parse Session and WebSockets

Hi Parse Community,

I’m having trouble to do two features with Parse. I’ve been exploring documentation, APIs and code source and I can’t find how to do these :

  1. If I revoke a sessionToken in the backend (so for example I remove a row of a currently logged in user), is there an event I can catch on the frontend to automatically force a logout

I tried to activate LiveQuery on the _Session class but if I try to log in I’m getting this error: Error: Cannot modify readonly attribute: sessionToken

  1. I’d like to be able to know the users who have an opened WebSocket connection with the parse server. Basically I’d like to be able to list the connected Users. Which is not equivalent to the Sessions since a Session is not deleted if a WebSocket is closed by the client.

I see in the source code that the parseWebSocketServer keeps a list of clients. I’m wondering if it’s possible to use it to retrieve those users.

Thanks in advance.

Thomas

As an answer to your first question, Parse SDK throws an Invalid Session Token error when session token is broke(or deleted in your case). When you are making a query or calling a cloud code, try to catch this error. So you can logout user.

Hi uzaysan,

Thanks for you reply. I’ve noticed the Invalid Session Token and I’m already catching this event on each query I’m doing in the front side. However, even though the session token has been removed in the db, if the user has opened websocket connections he will still receive some Parse.Object through liveQueries.
That’s why I’d like to force a logout as soon as the user session is deleted.

I dont know exactly this is what you want, But this is how I would do If I was ever gonna need what you need.

First add user to Parse.Installation in beoreSave Trigger:

Parse.Cloud.beforeSave(Parse.Installation, (request) => {
  request.object.set("user", request.user);
});

Then Use afterDelete Trigger for Session objects. Get user of session and send them a push notification.

Parse.Cloud.afterDelete(Parse.Session, (request) => {
  
  const query = new Parse.Query(Parse.Installation);

  const invalidUser = new Parse.User({id:request.object.get("user").id});

  query.equalTo("user",invalidUser);
  

  Parse.Push.send({
    where: query,
    data: { "type":"invalidSessionToken",
            "token":""+request.object.get("sessionToken")}
    }, { useMasterKey: true })
      .then(function() {
  // Push sent!
    }, function(error) {
  // There was a problem :(
  });
  
});

In your push reciever compare the type value and sessiontoken:

if(pushData.get("type").equals("invalidSessionToken"){
    if(pushData.get("token").equals(ParseUser.getCurrentUser().getSessionToken())){
         ParseUser.logOut();
    }
}

Something like this can be done

2 Likes

You just could add another class called Events (Or something like that). Then use Parse SDK to listen events collection via LiveQuery client. Send events to clients and if one of those is “session destroy” logout your user.

Thanks @uzaysan! I never used the push method, and I didn’t even known there was an Parse.Installation class. I’ll have a look into that, but since I want to be able to tell the client to logout also on a web app, I’m not sure if Parse.push is not limited to mobile app.

Hi @santiagosemhan,

That’s a really nice idea, I like it. Thanks for your input!

1 Like

At the end, I’ve used another approach to resolve my two points.

On the client app, I’m calling a cloud function every minute and store a lastActivityAt: Date field.

Thanks to this solution, if the session of the user is deleted the call will fail and so we can force a logout on the front side. And I can also analyse who is connected to my app by fetching all the Session with a recent User.lastActivityAt

It is kind of sad, that I have to use a setInterval solution, though.

Thanks for your help!

I am trying to implement afterDelete on Parse.Session but I can’t make the server to load the code. I simplified it to a bare minimum:

Parse.Cloud.afterDelete(Parse.Session, async (request) => {
    
    const installId = request.object.get('installationId');
    const userId = request.user.id;
    request.log.info(`session deleted installId ${installId}, userId ${userId}`);
    
});

it behaves the same no matter I use async in the afterDelete(Parse.Session, async (request) => or not. It shows in the system log:

Error loading your cloud code:
undefined

The following variant does not show that error but also do not trigger the event:

Parse.Cloud.afterDelete("Session", async (request) => {
    ...
});

I am aiming to implement a deletion of the Installation after the session is removed. In the afterLogout trigger the following works:

Parse.Cloud.afterLogout(async request => {
    const { object: session } = request;
    const query = new Parse.Query(Parse.Installation);
    const installId = session.get('installationId');
    const userId = session.get("user").id;
    query.equalTo("userId", userId); //userId is indexed
    query.equalTo("installationId", installId);
    
    query.find({useMasterKey: true}).then((installations) => {
		Parse.Object.destroyAll(installations, {useMasterKey: true});
	}).catch((error) => {
		request.log.info(`Error deleting related installations ${error.code}: ${error.message}`);
	});
  
});

But I would like to implement it in the Session’s afterDelete to cover also cases where user gets banned, deletes account, session is old and expired,…

Having look at the source code here it seems that only the afterLogout trigger is available to Parse.Session so I think that what was proposed above is not possible.

Please, correct me if I am wrong.

Yes. I believe that triggers currently do not work for Session class.

Related GitHub issue: Fail test for Login or Signup when LiveQuery server is set to watch for class _Session by Meglali20 · Pull Request #7495 · parse-community/parse-server · GitHub