Allow admin to "login as" any user


#1

I’m using the standard Meteor accounts package, along with accounts-password and what I’d like to do is to allow an admin of the application to be able to login as any other user, to help with fault diagnostics, technical support, etc. But without the admin needing to know the user’s password.

I was thinking that I could have something like Meteor.method('admin-login-as', function (username) {...}); as a method available only on the server. It would obviously do some checking to ensure that the user calling the function is indeed an admin, and then logout and login as the specified user. However I have no idea how the hell to allow a user to login without providing a password that matches! :confused:


How to impersonate a user?
#2

Check https://github.com/gwendall/meteor-impersonate


#3

Check here for what I think you want to do.

First, create a loginHandler that looks something like this:

Accounts.registerLoginHandler(function(loginRequest) {
  if(!loginRequest.admin) {
    return undefined;
  }

  // Probably want to do something else here... lol :)
  if(loginRequest.password != 'admin-password') {
    return null;
  }
  
  if(typeof loginRequest.user != 'string') {
    var user = Meteor.users.findOne({_id : loginRequest.user});
    if (!user) return undefined;
  } else {
    if (!user) return undefined;
  }

  // Send logged in user's Id
  return { id : user._id };
});

Next, define the login method.

Meteor.loginAsUser = function(password, callback) {
  //create a login request with admin: true, so our loginHandler can handle this request
  var loginRequest = {admin: true, password: password, user: user};

  //send the login request
  Accounts.callLoginMethod({
    methodArguments: [loginRequest],
    userCallback: callback
  });
};

You should be able to get something like this working.


#4

@gwendall’s method seems to be much better…


#5

Won’t this lose the “impersonation” when the user navigates around the application? Also, is there an easy way to “un-impersonate”?


#6

@Siyfion It doesn’t lose the impersonation as long as the user doesn’t hard refresh the page. No un-impersonation option yet but I’ll have to add it indeed.


#7

Awesome job! Especially as I too, use alanning:roles! Winner!


#8

All the credit goes to @dweldon! And btw, I just updated the package with the un-impersonate method.


#9

@gwendall I’ve added a pull request for a useful template helper function, also, I might have an issue with the data-unimpersonate flag, it seems to log me out entirely in my application, am I missing somthing?


#10

@Siyfion PR merged, thanks! And my bad for the bug, just published a fixed version.


#11

Awesome, good work! Between us, it’s now a pretty complete solution! :smiley:

EDIT: Oops, although I messed up the PR, I forgot that Impersonate._user isn’t a reactive source, so it doesn’t re-run when it changes.


#12

@Siyfion That’s how it should work, doesn’t it? When a user “un-impersonates”, it should log her back to her initial identity.

EDIT: I see, fixed that too. :smile:


#13

Take a look at my latest pull request! :smile:

You might need to also change the UI.registerHelper to Template.registerHelper as per http://docs.meteor.com/#/full/template_registerhelper

EDIT: Also, thanks for the rapid response! :+1:


#14

Cool, you’re welcome!


#15

@gwendall, would you kindly explain how your token system work? Are you sure services.resume.loginTokens[0].hashedToken cannot be forged by a malicious client? (line 64) What is services.impersonate.token? (line 55)


#16

@Steve Here is the main idea.

Users in given roles (through alanning:roles) are allowed to impersonate other users. As a user impersonates another one, everything will be “as if” it is this other user for Meteor. So we need to keep track of who this user originally was to know whether or not it can impersonate other users again after having “shape shifted” (to stop impersonating, for example).

On the first impersonation (https://github.com/gwendall/meteor-impersonate/blob/master/server/lib.js#L62), we know whether this user can impersonate or not. If yes, we can perform the impersonation. We then send a token down to the client so that we know that this “impersonated” user (that may not have impersonation rights itself) can now impersonate again.

On further impersonations (https://github.com/gwendall/meteor-impersonate/blob/master/server/lib.js#L52), we need to call the impersonation method with this token so that we can check the operation can actually be performed.

The token (services.resume.loginTokens[0].hashedToken) is stored in an in-memory variable on the client-side, hence is wiped out on page reload.

Hope this helps.


#17

Thanks, @gwendall.
What confuses me is line 55. When is services.impersonate.token actually written to Meteor.users? And by who?


#18

What’s wrong with just using this.setUserId(userId)? I’ve been using this for a while and it seems to work fine so far. If I want to stop impersonation I just manually refresh the page.


#19

Some might want a better user experience, like a “stop impersonation” button. Hence the token system.


#20

Right. This is on my task list.

What I planned on try to do was make a button that would somehow do a hard reset by either breaking the connection then reconnecting or somehow refreshing in a way that would reset the connection, thereby resetting the impersonation – it should be trivial no?