How can I get the Role for a user after Login? - c#

Here is my sample code for logging in:
if (WebSecurity.Login(playerModel.WorkerID, playerModel.Password))
{
if (Roles.IsUserInRole(User.Identity.Name, "Administrator"))
{
// display the admin panel
ViewBag.Message = "Admin Panel";
return View("Index");
}
else
{
ViewBag.ButtonText = "Log In";
ViewBag.Message = "You are not an Admin user";
return View();
}
}
For some reason, the first time I log in, the user is not in "Administrator" even though they are in the correct Role in the table. I have also tried using this:
Roles.GetRolesForUser();
That returns an empty array. I'm assuming there is a limitation to do with the user logging in, a post-back occurring and then being able to check the role. But I'm hoping there is a workaround (or I have something wrong).
After a post-back both the Roles.IsUserInRole and Roles.GetRolesForUSer methods work correctly.

you could try using
HttpContext.Current.User.IsInRole("role")
I'm not 100% sure on when the Roles security class gets updated after a login - but I bets its not until the forward page gets called and a whole new request is generated.

The solution was to redirect to another page and check there. The issue is that the cookie isn't read back in with the role information until you have another postback (read the cookie you just wrote).

Related

How to resolve: The provided anti-forgery token was meant for a different claims-based user than the current user

I am getting this error:
The provided anti-forgery token was meant for a different claims-based user than the current user.
and I am not sure how to correct this..
I have a MVC5 site and in this site I have a login page.
This is the scenario that it occurs on.
User AAA logs in. (No issues)
I attempt to access a view where the user does not have access.
I have the class decorated with an Authorize(Roles="aa")
The view then logs the user off and puts them back to the login page.
User AAA logs in. (This time I get the error mentioned above)
To note:
I am using customErrors and this is where I see the error message.
When I log the user out I am running this method:
[HttpGet]
public void SignOut()
{
IAuthenticationManager authenticationManager = HttpContext.GetOwinContext().Authentication;
authenticationManager.SignOut(MyAuthentication.ApplicationCookie);
}
Could I possibly be missing something on the SignOut?
UPDATE:
This only occurs because of step #2 listed above.
If I log in, then log out (calling same code) then log back in, then I do not have this issue.
I think you've neglected to post some relevant code. The Signout action you have returns void. If you were to access this action directly in the browser, then the user would get a blank page after being signed out with no way to progress forward. As a result, I can only assume you are either calling it via AJAX or calling as a method from another action.
The way anti-forgery works in MVC is that a cookie is set on the user's machine containing a unique generated token. If the user is logged in, their username is used to compose that token. In order for a new cookie, without a username to be set, the user must be logged out and a new request must occur to set the new cookie. If you merely log the user out without doing a redirect or something, the new user-less cookie will not have been set yet. Then, when the user posts, the old user-based cookie is sent back while MVC is looking for the new user-less cookie, and boom: there's your exception.
Like I said, you haven't posted enough code to determine exactly why or where this is occurring, but simply, make sure there is a new request made after logging the user out, so the new cookie can be set.
I was able to reproduce by clicking on the login button more than once before the next View loads. I disabled the Login button after the first click to prevent the error.
<button type="submit" onclick="this.disabled=true;this.form.submit();"/>
Disable the identity check the anti-forgery validation performs. Add the following to your Application_Start method:
AntiForgeryConfig.SuppressIdentityHeuristicChecks = true.
try:
public ActionResult Login(string modelState = null)
{
if (modelState != null)
ModelState.AddModelError("", modelState);
return View();
}
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model)
{
AuthenticationManager.SignOut();
return RedirectToAction("Login", "Controller", new { modelState = "MSG_USER_NOT_CONFIRMED" });
}
I haved similar problem. I found this text "#Html.AntiForgeryToken() " in my project in 2 place. And one plase will was in "view file" Views - test.cshtml.
#using (Html.BeginForm())
#Html.AntiForgeryToken()
<div class="form-horizontal">
<div class="form-group">
#if (#Model.interviewed)
...
I deleted this code line ("#Html.AntiForgeryToken() ") and working fine.
PS: But I am not delete this code in file _LoginPartial.cshtml.
Good luck!

Good way of storing UserProfile information and preventing database call on every page load

I am trying to create a way to store user's profile information in either session or cache and be able to have that information on hand every time the page loads to prevent calling the database every time we load a page. I want to be able to put some of these values in a shared layout (so i can put the user's name in the upper right hand corner etc.). At the top of my master page layout I have the following:
#{
Domain.Entities.UserProfile user = (Domain.Entities.UserProfile)HttpContext.Current.Session[Membership.GetUser().ProviderUserKey.ToString()];
}
I use values like user.FirstName in my master page. When a user logs in I set the session. Is there a better way of doing this? If I am logged out and I just navigate to a member only page, the [Authorize] filter is bypassed and I get an error Object reference not set to an instance of an object. because my user profile is not stored in session yet. I do not store any sensitive information in the session like a UserId or anything, just the users name, profile image url etc. I feel there may be a better way of doing such a task.
You can store your user information in the authentication cookie. Keep in mind that the cookie will be sent back and forth from the client with every request. So if there's a lot of information to be stored maybe that's not the right way.
Check the step 4 of this document that explains how to store additional user data in the cookie http://www.asp.net/web-forms/tutorials/security/introduction/forms-authentication-configuration-and-advanced-topics-cs
Then in your master page, you can have something like this
#{
if (User.Identity.IsAuthenticated)
{
//Your code goes here, no way user data is null.
}
You can create a static class as an interface to get the user properties, which will be stored in the session state.
public class UserData{
public static Domain.Entities.UserProfile GetUser(){
string username;
try{
username = Membership.GetUser().ProviderUserKey.ToString();
}catch(Exception e){
// just in case
throw new AuthenticationException("The user isn't authenticated!");
}
var user = HttpContext.Current.Session[username] as Domain.Entities.UserProfile;
if(user == null){
user = // get data from database
HttpContext.Current.Session[username] = user;
}
return user;
}
}
Then, in your methods you can use it like this:
if (User.Identity.IsAuthenticated)
{
UserProfile user = UserData.GetUser();
//Your code goes here, no way user data is null.
}
Or in controllers:
[Authorize]
public ActionResult Index(){
UserProfile user = UserData.GetUser();
return //stuff
}
Note: I made this without being able to test, but I hope you get the point :)

Set proxy user in a GenericPrincipal, while keeping the old identity, using MVC

I have a site where I allow some users to proxy in as an other user. When they do, they should see the entire site as if they where the user they proxy in as. I do this by changing the current user object
internal static void SetProxyUser(int userID)
{
HttpContext.Current.User = GetGenericPrincipal(userID);
}
This code works fine for me.
On the site, to proxy in, the user selects a value in a dropdown that I render in my _layout file as such, so that it appears on all pages.
#Html.Action("SetProxyUsers", "Home")
The SetProxyUsers view looks like this:
#using (#Html.BeginForm("SetProxyUsers", "Home")) {
#Html.DropDownList("ddlProxyUser", (SelectList)ViewBag.ProxyUsers_SelectList, new { onchange = "this.form.submit();" })
}
The controller actions for this looks like this
[HttpGet]
public ActionResult SetProxyUsers()
{
ViewBag.ProxyUsers_SelectList = GetAvailableProxyUsers(originalUserID);
return PartialView();
}
[HttpPost]
public ActionResult SetProxyUsers(FormCollection formCollection)
{
int id = int.Parse(formCollection["ddlProxyUser"]);
RolesHelper.SetProxyUser(id);
ViewBag.ProxyUsers_SelectList = GetAvailableProxyUsers(originalUserID);
return Redirect(Request.UrlReferrer.ToString());
}
All this works (except for the originalUserID variable, which I put in here to symbolize what I want done next.
My problem is that the values in the dropdown list are based on the logged in user. So, when I change user using the proxy, I also change the values in the proxy dropdown list (to either disappear if the "new" user isn't allowed to proxy, or to show the "new" user's list of available proxy users).
I need to have this selectlist stay unchanged. How do I go about storing the id of the original user? I could store it in a session variable, but I don't want to mess with potential time out issues, so that's a last resort.
Please help, and let me know if there is anything unclear with the question.
Update
I didn't realize that the HttpContext is set for each post. I haven't really worked with this kind of stuff before and for some reason assumed I was setting the values for the entire session (stupid, I know). However, I'm using windows authentication. How can I change the user on a more permanent basis (as long as the browser is open)? I assume I can't use FormAuthentication cookies since I'm using windows as my authentication mode, right?
Instead of faking the authentication, why not make it real? On a site that I work on we let admins impersonate other users by setting the authentication cookie for the user to be impersonated. Then the original user id is stored in session so if they ever log out from the impersonated users account, they are actually automatically logged back in to their original account.
Edit:
Here's a code sample of how I do impersonation:
[Authorize] //I use a custom authorize attribute; just make sure this is secured to only certain users.
public ActionResult Impersonate(string email) {
var user = YourMembershipProvider.GetUser(email);
if (user != null) {
//Store the currently logged in username in session so they can be logged back in if they log out from impersonating the user.
UserService.SetImpersonateCache(WebsiteUser.Email, user.Email);
FormsAuthentication.SetAuthCookie(user.Email, false);
}
return new RedirectResult("~/");
}
Simple as that! It's been working great. The only tricky piece is storing the session data (which certainly isn't required, it was just a nice feature to offer to my users so they wouldn't have to log back in as themselves all the time). The session key that I am using is:
string.Format("Impersonation.{0}", username)
Where username is the name of the user being impersonated (the value for that session key is the username of the original/admin user). This is important because then when the log out occurs I can say, "Hey, are there any impersonation keys for you? Because if so, I am going to log you in as that user stored in session. If not, I'll just log you out".
Here's an example of the LogOff method:
[Authorize]
public ActionResult LogOff() {
//Get from session the name of the original user that was logged in and started impersonating the current user.
var originalLoggedInUser = UserService.GetImpersonateCache(WebsiteUser.Email);
if (string.IsNullOrEmpty(originalLoggedInUser)) {
FormsAuthentication.SignOut();
} else {
FormsAuthentication.SetAuthCookie(originalLoggedInUser, false);
}
return RedirectToAction("Index", "Home");
}
I used the mvc example in the comments on this article http://www.codeproject.com/Articles/43724/ASP-NET-Forms-authentication-user-impersonation to
It uses FormsAuthentication.SetAuthCookie() to just change the current authorized cookie and also store the impersonated user identity in a cookie. This way it can easily re-authenticate you back to your original user.
I got it working very quickly. Use it to allow admin to login as anyone else.

Deleted User logged in even after deleting on the other broswer

var query = from p in AdminModelContext.Users
where p.UserName == model.UserName && p.Password == encryptPassword
&& p.IsDeleted == false
select p;
IList<Users> userList = query.ToList();
if (userList.Count() > 0)
{
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
if (CheckUrl(returnUrl))
{
return Redirect(returnUrl);
}
SetRoleForUser(userList[0].RolesId);
LoggerService.Info(string.Format("Login Successful for the user : {0}",
model.UserName));
return RedirectToAction("Index", "Home");
}
I am using the following code to Login through my website. The problem that I am facing is when I logged in with a user on a specific browser and at the same time login with a different user on a different browser, and then I delete the user(logged in on the other browser). Still I am able to navigate through the pages with the deleted user logged in.
I am not finding a fair solution to put authentication logic on every page. My website is in MVC model and using Form based authentication.
Please suggest how can I put the logged in user session validation and achieve this.
None of the answers so far actually acknowledge the question.
Lets look at the control flow:
User A enters log in page, supplies valid credentials
User A is issued Ticket A.
User B enters site, supplies valid credentials.
User B is issued Ticket B.
User B then revokes User A's access CREDENTIALS.
At this point nothing happens to Ticket A. Because the Ticket is independent on the credentials. When Ticket A expires they will then be required to present their credentials and it will fail login.
So what you've noticed that kicking a live user out of your site is actually pretty hard. As you've realized the ONLY solution is to have authentication logic on EVERY request. That unfortunately is really heavy.
In the login system I built I handled this aspect by creating 2 tickets, 1 ticket that's stored in the Forms Auth ticket as normal that has a big duration, and a ticket that's stored in HttpRuntime.Cache, I set the cache expiration to 15 minutes on this ticket.
On every page request I check to see whether a user has a ticket in the cache (based off their Forms Auth ticket information), at this point if they have no ticket I do a user data refresh and poll the user database. If the user has become suspended or deleted they will be logged out at then.
Using this method I know that my site can disable a user and within 15 minutes that user will be barred from the site. If I want them immediately barred I can just cycle the app config to clear the cache and FORCE it to happen.
Normally if you have the [Authorize] attribute defined for an Controller or an Action the Authentication is checked on every post back.
The build in MembershipProvider handles all that for you. But it seems you are using your own user Database. Then you have to implement your own MembershipProvider, IPrincipal and MembershipUser and have this added to your Web.config replacing the default one.
More you'll find here how to implement your own MembershipProvider: http://msdn.microsoft.com/en-us/library/f1kyba5e.aspx
My suggestion is to create an empty MVC project and have a look at the default authentication mechanism. And if your building a new Application with a new Database, try to use the default authentication.
Your validateUser function in your own MembershipProvider could look like this.
public override bool ValidateUser(string username, string password)
{
bool isValid = false;
bool isApproved = false;
string pwd = "";
using (AdminModelContext db = new AdminModelContext())
{
var user = db.Users.FirstOrDefault(u => u.UserName == username);
if (user != null)
{
pwd = user.Password;
isApproved = user.IsApproved;
if (CheckPassword(password, pwd))
{
if (isApproved)
{
isValid = true;
user.LastLoginDate = DateTime.Now;
user.LastActivityDate = DateTime.Now;
try
{
db.SubmitChanges();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
}
else
{
UpdateFailureCount(username, "password");
}
}
}
return isValid;
}
I see the problem now. I dont know how this works in MVC, but by using Authenticate_Request, you can validate if the user is still valid. The business logic may also double check if the user is still valid. But as far as I know, there is no way of iterating all the open sessions and killing the requierd ones, even in that case, the authorization cookie should be double checked on Session_Start event.
Another options is adding a global invalidated_users list on the application, and then checking that user against the invalid list. This list should only contain users that are invalidated after the application has restarted.
Link for Reading All Users Session:
http://weblogs.asp.net/imranbaloch/archive/2010/04/05/reading-all-users-session.aspx

How can I set up ASP.NET login to allow the UserName or UserId to be retrieved later on in the session?

I'm trying to create a login system for my website, I've created a custom login.ascx and when the user clicks [ Login ] a div pops up with the contents of login.ascx.
Then after the user enters their credentials, they click on the Login button. They get validated and logged in using this code in the login click function:
if( Membership.ValidateUser( userName.Text, password.Text ) )
{
//Here is where I'm not sure what to do
}
else
{
LoginError.Visible = true;
}
So in the section where I'm not sure what to do, I would like the user to get logged in (Not sure if that means creating an authentication ticket or whatnot). What does is the next step to actually log the user in, I don't want them to get redirected anywhere since they are on the correct page already.
I would also like to be able to retrieve their user name or user id later on for use in my web services. So, for this should I do a Session.Add to create a new session value or is there some other way of storing the data that is preferred?
For authenticating the user,
FormsAuthenatication.SetAuthCookie(username, false/*true if you want to remember the user's login*/);
This logs the user in. You can later use
Page.User.Identity.Name
to retrieve username of the current user and
Page.User.Identity.IsAuthenticated
to check if the user is logged in.
There's no need to store it in Session. Just use:
FormsAuthentication.SetAuthCookie
to send an authentication ticket to the client. Then use HttpContext.Current.User.Identity to retrieve it later.
I find using the membership provider is useful, I would recommend it
Scott Guthrie posted great blog on this
http://weblogs.asp.net/scottgu/archive/2006/05/07/ASP.NET-2.0-Membership-and-Roles-Tutorial-Series.aspx

Categories