user2602152
user2602152

Reputation: 797

How can I log out a user from the server in Meteor.js?

In my Meteor.js application, I want to let the administrator be able to forcibly log out users.

The use case is that my application is serving end-users, and the service is open whenever a super-user is logged in. If a super-user has forgotten to explicitly log out, the service will seem to be open for the end-users. If an administrator sees this, he/she should be able to forcibly log out the logged in user, and such render the service closed for the end-users.

Is this possible with Meteor.js? If so, how? Are there better/other approaches to this use case?

EDIT: Added some examples of remote logout I have tried, to clarify for @Akshat.

EXAMPLE 1(which does not work as I want):

In a logout method:

if (user.profile.role === ROLES.ADMIN) {
            Meteor
                .users
                .update({
                    _id: options.userId
                }, 
                { 
                    $set: { 
                        'services.resume.loginTokens' : [] 
                }});
        } else {
            throw new Meteor.Error(403, "You are not allowed to access this.");
        }

In my application.js:

var lastUserId;
Deps.autorun(function () {
    if(Meteor.user()) {
        if (Meteor.user().profile && Meteor.user().profile.firstName) {
            console.log("USER LOGGED IN");
            console.log("LENGTH LOGINTOKENS", 
                    Meteor
                        .user()
                        .services
                        .resume
                        .loginTokens.length); // This is always 1
            lastUserId = Meteor.user()._id;

            if (Meteor.user().services.resume.loginTokens.length === 0) {
                // This never fires, and thus the client does not know until
                // manually refreshed. Of course I could keep a forceLogOut-variable
                // as done in the next example.
                window.location.reload();
            }
        }
    } else {
        console.log("SOMETHING CHANGED IN METEOR.USER");
        if (lastUserId) {
            console.log("THE USER IS LOGGED OUT");
            Meteor.call('userLoggedOut',
            {
                userId: lastUserId
            });
            lastUserId = null;
        }
    }
});

EXAMPLE 2(this works as I want, when using only forceLogOut together with Meteor.logout() on the client side.):

In a logout method:

if (user.profile.role === ROLES.ADMIN) {
            Meteor
                .users
                .update({
                    _id: options.userId
                }, 
                { 
                    $set: { 
                        'services.resume.loginTokens' : [],
                        'profile.forceLogOut': true
                }});
        } else {
            throw new Meteor.Error(403, "You are not allowed to access this.");
        }

In my application.js:

var lastUserId;
Deps.autorun(function () {
    if(Meteor.user()) {
        if (Meteor.user().profile && Meteor.user().profile.firstName) {
            console.log("USER LOGGED IN");
            console.log("LENGTH LOGINTOKENS", 
                    Meteor
                        .user()
                        .services
                        .resume
                        .loginTokens.length); // This is always 1
            lastUserId = Meteor.user()._id;

            if (Meteor.user().profile.forceLogOut) {
                // Small example 1:
                // When logintokens have been set to [], and forceLogOut
                // is true, we need to reload the window to show the user
                // he is logged out.
                window.location.reload();
                // END Small example 1.

                // Small example 2:
                // When already keeping this variable, I might as well just use
                // this variable for logging the user out, and no resetting of
                // loginTokens are needed, or reloading the browser window.
                // This seems to me as the best way.
                console.log("FORCING LOGOUT");
                Meteor.logout();
                // END Small example 2.

                // And finally resetting the variable
                Meteor.call('resetForceLogOut',
                    {
                        userId: Meteor.user()._id
                    });
           }
        }
    } else {
        console.log("SOMETHING CHANGED IN METEOR.USER");
        if (lastUserId) {
            console.log("THE USER IS LOGGED OUT");
            Meteor.call('userLoggedOut',
            {
                userId: lastUserId
            });
            lastUserId = null;
        }
    }
});

Upvotes: 14

Views: 8032

Answers (3)

pgorecki
pgorecki

Reputation: 689

I had a similar issue recently - after deleting resume token of currently logged in user, Tracker.autorun wasn't able to (and it should be in such a case) detect changes in Meteor.userId() as in the following code:

Tracker.autorun(() => {
  console.log('currrent user', Meteor.userId());
});

It turned out that the problem was caused by one of the publications which logged in user subscribed. Somewhere in the routing-related code (I was using Iron router), I had:

export const AppController = RouteController.extend({
  waitOn() {
    if (!Meteor.userId()) {
      return Router.go('signIn');
    }
    Meteor.subscribe('data'),
  },
});

And the publication code was:

Meteor.publish('data', function () {
  if (!this.userId) {
    return; // this line caused the logout bug
  }
  return MyCollection.find();
}

The bug was caused by returning undefined (instead of an empty array) when this.userId was not set, which caused the publication to never be ready. For some strange reason, this also caused Tracker not to fire when resume token was deleted.

So the fix was:

Meteor.publish('data', function () {
  if (!this.userId) {
    return []; // this is the correct way to return empty set
  }
  return MyCollection.find();
}

Upvotes: 1

cenk
cenk

Reputation: 1429

Log out everyone but not the current user (you):

Meteor.users.update({
    _id: {
        $ne: Meteor.user()._id
    }
}, {
    $set: {
        "services.resume.loginTokens": []
    }
}, {
    multi: true
});

Upvotes: 3

Tarang
Tarang

Reputation: 75945

You have to delete all the loginTokens from the database. It will do it for all users with this query. You can customize the selector if you want to log out a smaller subset of users or exclude the current user.

Meteor.users.update({}, {$set : { "services.resume.loginTokens" : [] }}, {multi:true});

A couple of things:

  • This will log out all users including the current logged in user.
  • It only works on the server side

Upvotes: 27

Related Questions