I am creating an application using MVC4. I am trying to work out how to capture when a user logs in.
I thought I could add some code in the login Action in Account controller..
public ActionResult Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid && _authProvider.Login(model.UserName, model.Password, true))
{
var currentUser = Membership.GetUser();
return RedirectToLocal(returnUrl);
}
// If we got this far, something failed, redisplay form
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View(model);
}
but the currentUser is always null? why is that? How can I fix it?
Thanks
Membership.GetUser() doesn't work because it uses the HttpContext.Current.Identity to retrieve the the user, but this will not get set until the next page refresh.
The reason is that your Login method most likely uses FormsAuthentication.SetCookie() (or some variation) to create an auth ticket, but that ticket has to be read on a page load to be valid. Since the current page is already loaded at this point, authentication is not valid until the next page load.
Regardless, I don't understand why you even need to do this. You already know the users name. It's right there, you pass it to the Login method... model.UserName.
Related
Scenario: User1 with "View" permissions can see all other user profiles in a list and when they click on a profile they will be directed to that profile page.
User2 with no permissions can see a list of user profiles but can only click on their own profile to view otherwise they are redirected to forbidden.
Not sure of the best method to implement this Authorization check. Currently trying to use the answer from this question: Adding .Net Core Policy with variable Parameters on Requirements
but I don't know how I could send the id parameter from this controller method and also check first if the user does have the "View" permission
[HttpGet("{id:int:min(1)}")]
[CustomAuthorizeAttribute(id)] // doesnt work
public async Task<ActionResult> GetUser(int id)
In that solution, The parameter is a certain value in development, not obtained from routing. [CustomAuthorizeAttribute(id)] is scanned when project starts, it is different form the [HttpGet("{id:int:min(1)}")].
In this scenario, I think you don't need to define the CustomAuthorizeAttribute. If one user can view all the profiles, the user need to be added the claim View when generating token.
new Claim("Permission", "View")
When in GetUser, you only need to judge whether the user has the claim.
[HttpGet("{id:int:min(1)}")]
public async Task<ActionResult> GetUser(int id)
{
if(User.Claims.Contains(new Claim("Permission", "View")))
{
//...
return View();
}else if(User.Claims.Contains(new Claim("id", id.ToString())))
{
//own id can view own profile
return View();
}
else
{
return Forbid();
}
}
Using VS 2013, standard MVC template, and the Identity provider framework
The user is logged in, and I have:
//....
UserManager.AddToRole(User.Identity.GetUserId(), "Members"); # Line X
RedirectToAction("Index", "Members");
And the Members controller is as follows:
[Authorize(Roles="Members")]
public class MembersController : Controller
{
// GET: Members
public ActionResult Index()
{
return View();
}
}
After Line X is executed, I can confirm that the user is added to the table dbo.AspNetUserRoles. However, the user upon reaching the Members controller fails the role check. User.IsInRole("Members") returns false.
If the user logs off and then logs in again, then access to the Members controller will go through, ie User.IsInRole("Members") now returns true.
Is there some caching? Why the delay? How do I overcome it?
I also tried converting the method at Line X to an async method and used UserManager.AddToRoleAsync. The same delayed effect is still there.
The identity information(roles,claims) are put into the cookie when the user logs in. Since the user is already logged in, this line of code UserManager.AddToRole(User.Identity.GetUserId(), "Members") will update the db , but not the cookie. You have to have to re-issue the cookie.
Try add SignInManager.SignIn(user, false, false); (if you dont have user, var user = UserManager.FindById(User.Identity.GetUserId())) before RedirectToAction("Index", "Members");
So in my .net application I have three controllers
Home
Kitchen
Institution
When the user logs in, I get what controller the user can go. I have defined some roles and using those I get which user needs to go where.
So for example if a user Bob is a cook. When Bob logs in, he is taken to the kitchen dashboard. But if Bob types in the URL ../Home/Dashboard there is nothing stopping him for going in there. What should be done in order to restrict Bob accessing any other url?
Also, when the user logs in should I store the information about his role in session?
What is the best practice for this purpose?
You can handle all your requirements in an HttpPost Login Action. See comments for further instructios.
[HttpPost]
[AllowAnonymous]
public ActionResult Login(LoginModel model, string returnUrl)
{
// check first for field validations
if (!ModelState.IsValid)
return View(model);
// validate user agains database
var user = FindUser(model.UserName, model.Password);
if (user == null)
{
ModelState.AddModelError("", "Invalid username or password.");
return View(model)
}
// user is valid, sign in assuming forms authentication, however it's
// best practice to abstract following statement by use of some kind of authentication
// manager (refer to OWIN framework for a better approach).
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
// when returnUrl param is provided
if (!String.IsNullOrEmpty(returnUrl))
return Redirect(returnUrl);
// following 'if' - condition dependent on your domain models.
if (user.IsCook)
return RedirectToRoute(/*kitchen route*/);
else
return RedirectToRoute(/*dashboard*/);
}
I have one View called SignIn that contains two partial views for authentication. One is for OpenID and the other is for logging in to my site using an internal account.
The Action for the OpenID form goes to an OpenIDSignIn() Action while the other just points to SignIn(). Everything works great except for when a user clicks a link to a view that requires them to be logged on [Authorize] etc..
I see the returnUrl in the QueryString however this value is not available to the current controller action, due to the fact that they will be either be logging in with OpenID or normally, thus caling the ActionResult associated with either of those views.
Just for clarification the returnUrl is the one that gets thrown in there from FormsAuthentication and is used when doing a RedirectFromLoginPage etc..
public ActionResult SignIn(string returnUrl)
{
if(!string.IsNullOrEmpty(returnUrl))
{
if(UrlUtil.IsLocalUrl(returnUrl))
{
Session.Add("ReturnUrl", returnUrl);
}
else
{
return RedirectToAction("SignIn", "Account");
}
}
return View();
}
This seems to be a viable workaround. However I hate session variables!
Im getting really lost on how to use HttpContext.User. I read everywhere that its great for FormAutherication, but i just cant see how it works. If i do something like this:
ControllerContext.HttpContext.User = new GenericPrincipal(GetUser(username, password), roles);
What does ControllerContext.HttpContext.User contain? and how do i access information about the user this way?
Im think that i have a Action like this:
public User GetUser(string username, string password)
{
try
{
var user = (from u in dm.Users
join r in dm.Roles
on u.Role_ID_FK equals r.RoleID
where u.Username.Equals(username) && u.Password.Equals(password)
select u).Single();
return user;
}
catch (Exception e)
{
return null;
}
}
And then if i want user information in my view, like the user name or role, i can call ControllerContext.HttpContext.User.Username in my View. But this is diffenrently the wrong way to look at it.
So can you guys give me a kick in the rigth direction or post a link to a site which can?
I'm not sure exactly what you are trying to do with the code you posted, but here's some help with HttpContext.User. In layman's terms it represents the current user requesting the particular page, and actually within your Controller you can just reference it as "User" without the prefix.
User.Identity will let you know if the user is authenticated, and if so their username and how they authenticated (Forms or Windows).
It's generally used to get the username of the user requesting the page so your controller actions can perform the correct duties. Something like:
public ActionResult Index()
{
//you should probably use the [Authorize] attribute on the Action Method
//but you could check for yourself whether the user is authenticated...
if (!User.Identity.IsAuthenticated)
return RedirectToAction("LogIn");
MyUser u = repository.GetUser(User.Identity.Name); //lookup user by username
ViewData["fullname"] = u.FullName; //whatever...
return View();
}
In this example, if the user hasn't been authenticated, they will be redirected to a LogOn page, and if they have been, the Action method is using the User.Identity.Name (which is the username they logged in with, or their Windows login) to lookup and return an instance of a MyUser object from your database and puts the user's full name in ViewData to be displayed.
In your login code use:
FormsAuthentication.SetAuthCookie("userName", remeberMe);
to set the authenticated user, then you can use
<%= User.Identity.Name %>
<%= User.IsInRole("role") %>