What is the "Login As" Design Pattern or Feature?
If one is authorized, they can impersonate (login as) another user without providing a password. I believe it should be in every application as it helps a lot in investigating production issues.
Why do we use this?
In many situations (mostly for debugging purposes), we want to login as another user to see what that user is seeing or facing. This helps us in understanding the problem quickly and fixing it. If it is working at your end, it would not be an application issue but user environment or local settings issues.
Where do we need this?
Almost in every application wherever we've some authentication mechanism, we may need to investigate production issues.
Is this not dangerous?
Yes it can be. Therefore careful development & proper logging of actions should be done. Sensitive Actions can be restricted when the user is in "Login As" mode. Normally read actions should be allowed but if write actions need to be allowed, detail of actual user should be saved too.
Assumption
We have a service which provides the following functions for user management.
- AuthenticateUser(Login,Password): Takes Login & Password and returns User Object is user is valid
- GetUser(ID) : Takes User ID & returns User Object
How do we do this in (Session based) Web Applications ?
Note
Although the sample code is showing C# syntax but the concept is independent of language or development framework.
Normally we use Session in web applications to manage state between multiple requests. We keep something in session when the user is authenticated and use that information later to check on authorized pages if the user has been authenticated or not. For example, a user is authenticated and we stored the following information. Here userObj is User Object which we got from AuthenticateUser Method
- var userObj = service.AuthenticateUser(Login,Password);
- String login = userObj.Login;
- Boolean canLoginAs = userObj.CanLoginAs;
-
-
-
- Session["ID"] = userObj.ID;
- Session["Login"] = login;
- Session["CanAsLogin"] = canLoginAs;
In the above code, we have stored information whether a user can use Login As feature or not. This permission came in User Object.
We can use Session ["CanAsLogin"] to decide if the current user is authorized to use "Login As" feature or not. Based on this flag, we can enable Login As Panel. Login Panel is like Login screen but without password and takes User ID (You may change it according to your authentication system). Let's suppose we want to LoginAs with ID=150. We'll provide input and click on "Login As" button. On the server side, we'll make a call to GetUser() function and will get User Object. Here is an updated version of the above code.
- var userObj = null;
- if (NormalLogin == true) {
- userObj = service.AuthenticateUser(Login, Password);
- } else if (LoggedInAsFeature == true)
- {
- userObj = service.GetUser(user_provided_input);
- }
- String login = userObj.Login;
- Boolean canLoginAs = userObj.CanLoginAs;
- if (LoggedInAsFeature == true)
- {
- Session["IsLoginAs"] = true;
- Session["OldID"] = Session["ID"];
- login = login + "_" + Session["Login"];
- canLoginAs = false;
- }
- Session["ID"] = userObj.ID;
- Session["Login"] = login;
- Session["CanAsLogin"] = canLoginAs;
Note that we've introduced two new data members in session to track original user information. Also, on the front end, we'll show "Login Back" option instead of "Login As". Now we are fully logged in with ID=150. We would be using Session["Login"] in our transactions to log who is creating of modifying a record. Anyone later can see from the logs who was an actual user & which was a "Login As" user.
Login Back Feature?
Here is sample code to explain all the above cases. It is not optimized or final code. This is just to give you an idea of the concept. Change or optimize according to your need.
- var userObj = null;
- if (NormalLogin == true) {
- userObj = service.AuthenticateUser(Login, Password);
- } else if (LoggedInAsFeature == true) {
- userObj = service.GetUser(user_provided_input);
- } else if (LoggedBackAsFeature == true) {
- int id = Session["OldID"];
- userObj = service.GetUser(id);
- }
- String login = userObj.Login;
- Boolean canLoginAs = userObj.CanLoginAs;
- if (LoggedInAsFeature == true)
- {
- Session["IsLoginAs"] = true;
- Session["OldID"] = Session["ID"];
- login = login + "_" + Session["Login"];
- canLoginAs = false;
- } else if (LoggedBackAsFeature == true)
- {
- Session["IsLoginAs"] = false;
- Session["OldID"] = null;
- }
- Session["ID"] = userObj.ID;
- Session["Login"] = login;
- Session["CanAsLogin"] = canLoginAs;
Summary
"Login As" allows you to do this investigation on your machine. Very small changes are required in the application but these should be done carefully and proper logging should be maintained.