Essentially I'm trying to set a cookie after a user logs in to persist their username for the next time they log in. Here's my code to set the cookie. When I look at the site cookies in Firefox as soon as the cookie is set, it shows the sessionID cookie, but not the one I just set. When I check the headers in Fiddler, I don't see it setting the cookie, only my sessionID cookie.
HttpCookie hc = new HttpCookie("username", model.UserName);
hc.Expires = DateTime.Now.AddYears(1);
System.Web.HttpContext.Current.Request.Cookies.Add(hc);
Here is where I check to see if the cookie exists.
if (System.Web.HttpContext.Current.Request.Cookies["username"] != null)
Here's the full context of the methods in question
public ActionResult LogOn()
{
if (System.Web.HttpContext.Current.Request.Cookies["username"] != null)
return View(new LogOnModel { UserName = System.Web.HttpContext.Current.Request.Cookies["username"].Value });
else
return View();
}
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
if (MembershipService.ValidateUser(model.UserName, model.Password))
{
HttpCookie hc = new HttpCookie("username", model.UserName);
hc.Expires = DateTime.Now.AddYears(1);
System.Web.HttpContext.Current.Request.Cookies.Add(hc);
FormsService.SignIn(model.UserName, model.RememberMe);
if (!String.IsNullOrEmpty(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
return View(model);
}
Add to response.cookies not request.cookies
Related
I'm using FormsAuthentication for logging. What I want is when user checks "Remember Me" option, make cookie never expire. My problem is whenever I try to change cookie expiration time either Request.IsAuthenticated becomes false withResponse.Cookies[FormsAuthentication.FormsCookieName].Value equals null or Request.IsAuthenticated returns always true. My codes are below.
[HttpPost]
[AllowAnonymous]
public ActionResult Login(PortalUserModel model)
{
if (ModelState.IsValid)
{
uzm_portaluser user = _defDal.GetPortalUser(model.Username, model.Password);
if (user != null)
{
FormsAuthentication.SetAuthCookie(user.Id.ToString(), true);
if (model.isRemember)
{
//Tried to increase expire time here using various ways but none of them worked
}
System.Web.HttpContext.Current.Session["user"] = user;
return RedirectToAction(defaultAction, defaultController);
}
else
{
ViewBag.Error = "Wrong username or password";
return RedirectToAction("Login", "Home");
}
}
else
{
ViewBag.Error = "Wrong username or password";
return RedirectToAction("Login", "Home");
}
}
I tried these but as I said none of them worked.
https://stackoverflow.com/a/4851344
(This one makes Request.IsAuthenticatedtrue but Response.Cookies[FormsAuthentication.FormsCookieName] is empty. Most of the solutions are like this)
https://stackoverflow.com/a/10773495/2564339
(This one did not work at all)
I have to get roles of a user from DB as per my application nature. I am authenticating user in context.AcquireRequestState += context_AcquireRequestState; event handler in HttpModule. Can I do db calls from HttpModule to assign roles in Identity? Is it good practice? if not, where I have to do it before controller’s action method called.?
I dont know what you doing with Aquaire request state, Ideally you have to do as below:
[Authorize(Roles="Admin")]
[Route("user/{id:guid}/roles")]
[HttpPut]
public async Task<IHttpActionResult> AssignRolesToUser([FromUri] string id, [FromBody] string[] rolesToAssign)
{
var appUser = await this.AppUserManager.FindByIdAsync(id);
if (appUser == null)
{
return NotFound();
}
var currentRoles = await this.AppUserManager.GetRolesAsync(appUser.Id);
var rolesNotExists = rolesToAssign.Except(this.AppRoleManager.Roles.Select(x => x.Name)).ToArray();
if (rolesNotExists.Count() > 0) {
ModelState.AddModelError("", string.Format("Roles '{0}' does not exixts in the system", string.Join(",", rolesNotExists)));
return BadRequest(ModelState);
}
IdentityResult removeResult = await this.AppUserManager.RemoveFromRolesAsync(appUser.Id, currentRoles.ToArray());
if (!removeResult.Succeeded)
{
ModelState.AddModelError("", "Failed to remove user roles");
return BadRequest(ModelState);
}
IdentityResult addResult = await this.AppUserManager.AddToRolesAsync(appUser.Id, rolesToAssign);
if (!addResult.Succeeded)
{
ModelState.AddModelError("", "Failed to add user roles");
return BadRequest(ModelState);
}
return Ok();
}
Source read here
I have this method here, which is a login method and if the username and password is incorrect a ModelError gets added.
[HttpPost]
public ActionResult Login(LoginClass model, string ReturnUrl)
{
if (ModelState.IsValid)
{
if (Membership.ValidateUser(model.UserName, model.Password))
{
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
if (Url.IsLocalUrl(ReturnUrl) && ReturnUrl.Length > 1 && ReturnUrl.StartsWith("/")
&& !ReturnUrl.StartsWith("//") && !ReturnUrl.StartsWith("/\\"))
{
return Redirect(ReturnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect");
}
}
return RedirectToAction("Index", "Home");
}
My question is how would I display the ModelError to my View Index.cshtml?
... how would I display the ModelError to my View Index.cshtml?
Showing Error message
I initially made the assumption that you wanted to redirect to the home page (HomeController action Index) based on your call to return RedirectToAction("Index", "Home"); at the bottom of the Login action. Now I am thinking maybe this is a mistake in your flow and you are actually trying to show the error message to the user without redirecting and that you only want to redirect if everything succeeds. IF this is the case then just read this part and skip the rest about how to persist model state across RedirectToAction calls. All you need to do is call View instead of RedirectToAction if there is a failure.
[HttpPost]
public ActionResult Login(LoginClass model, string ReturnUrl)
{
if (ModelState.IsValid)
{
if (Membership.ValidateUser(model.UserName, model.Password))
{
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
if (Url.IsLocalUrl(ReturnUrl) && ReturnUrl.Length > 1 && ReturnUrl.StartsWith("/")
&& !ReturnUrl.StartsWith("//") && !ReturnUrl.StartsWith("/\\"))
{
return Redirect(ReturnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect");
}
}
return View(model); // goes back to the login view with the existing model and validation error
// return RedirectToAction("Index", "Home");
}
Login.cshtml include the following somewhere
#Html.ValidationSummary()
Working with RedirectToAction
The reason it is not working is the ViewData, which includes the validation messages, is lost when you execute the RedirectToAction. There are a couple of options for a solution.
Keep either persist the ViewData across the RedirectToAction and then restore it.
Only persist the ModelState and then merge it with the ModelState once the new action specified in the RedirectToAction has executed.
Persist ViewData
In most scenarios this works just fine but could cause problems if your redirected to action (in this case Index on the HomeController) has its own ViewData that it depends on.
LoginController.cs
// simplified code to just show the relevant parts to reproduce the problem/solution
[HttpPost]
public ActionResult Login(LoginClass model, string ReturnUrl)
{
// ... some other code
ModelState.AddModelError("", "The user name or password provided is incorrect");
// ... some other code
if (!ModelState.IsValid)
TempData["ViewData"] = ViewData;
return RedirectToAction("Index", "Home");
}
HomeController.cs
public ActionResult Index()
{
if (TempData["ViewData"] != null)
{
// restore the ViewData
ViewData = (ViewDataDictionary)TempData["ViewData"];
}
return View();
}
Home\Index.cshtml
#Html.ValidationSummary()
Merge the ModelState
This is would be my recommended approach because you define how you want this to happen once on a custom ActionFilter attribute and then apply where you want it to occur. You could also put this code directly into your controller but that would violate the DRY principle as soon as you need to do this on multiple controllers.
The approach here is to write the model state to the TempData if the TempData does not already contain a "ModelState" key. If there is already a key present that means that the current request has just written to it and we can read from it and merge that with our existing model state. This will prevent the code from unintentionally overwriting the ViewState or the ModelState as the ModelState is now merged. I can only see this going wrong if there are multiple RedirectToActions that all choose to write to the ModelState but I do not think this is a likely scenario.
ModelStateMergeFilterAttribute.cs
public class ModelStateMergeFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
// write to the temp data if there is modelstate BUT there is no tempdata key
// this will allow it to be merged later on redirect
if (filterContext.Controller.TempData["ModelState"] == null && filterContext.Controller.ViewData.ModelState != null)
{
filterContext.Controller.TempData["ModelState"] = filterContext.Controller.ViewData.ModelState;
}
// if there is tempdata (from the previous action) AND its not the same instance as the current model state THEN merge it with the current model
else if (filterContext.Controller.TempData["ModelState"] != null && !filterContext.Controller.ViewData.ModelState.Equals(filterContext.Controller.TempData["ModelState"]))
{
filterContext.Controller.ViewData.ModelState.Merge((ModelStateDictionary)filterContext.Controller.TempData["ModelState"]);
}
base.OnActionExecuted(filterContext);
}
}
LoginController.cs
// simplified the code to just show the relevant parts
[HttpPost]
[ModelStateMergeFilter]
public ActionResult Login(LoginClass model, string ReturnUrl)
{
ModelState.AddModelError("", "The user name or password provided is incorrect");
return RedirectToAction("Index", "Home");
}
HomeController.cs
[ModelStateMergeFilter]
public ActionResult Index()
{
return View();
}
Home\Index.cshtml
#Html.ValidationSummary()
References
Here are some references that also detail some of these approaches. I also relied on some of the input from these previous answers for my answer above.
How do I maintain ModelState errors when using RedirectToAction?
How can I maintain ModelState with RedirectToAction?
If your are using MVC, you can use Validation Summary.
#Html.ValidationSummary();
https://msdn.microsoft.com/en-CA/library/dd5c6s6h%28v=vs.71%29.aspx
Also,pass your model back to your view:
return RedirectToAction("Index", "Home", model);
I want to know what set's the user identity name and change isAuthenticatedto true.
Why is User.Identity.Name an empty string and User.Identity.IsAuthenticated false after SignInManager.PasswordSignInAsync has returned Success.
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
var userIdentityNameTest = User.Identity.Name; // Empty string
var result = await SignInManager.PasswordSignInAsync(
model.Email, model.Password,
model.RememberMe, shouldLockout: false);
// result is "Success"
userIdentityNameTest = User.Identity.Name;
// userIdentityNameTest is still an empty string?
// User.Identity.IsAuthenticated is still false?
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl,
RememberMe = model.RememberMe });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
}
It seems that SignInManager.PasswordSignInAsync only validates entered data and run AuthenticationManager.SignIn if you are not using TwoFactorAuthentication. AuthenticationManager.SignIn in this case only set authentication cookie to response.
So, User.Identity is available in subsequent requests to your application. To get ApplicationUser by Name you can use ApplicationUserManager as follows:
UserManager.FindByNameAsync(model.Name)
I am Implementing Social login in my application but I am getting an error "No parameterless constructor defined for this object".I am not able to find out the reason behind this.can any one tell me exactly what haapening.My Controller is as follows:
[AllowAnonymous]
public ActionResult ExternalLoginCallback(string returnUrl)
{
AuthenticationResult result = OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
if (!result.IsSuccessful)
{
return RedirectToAction("ExternalLoginFailure");
}
if (OAuthWebSecurity.Login(result.Provider, result.ProviderUserId, createPersistentCookie: false))
{
return RedirectToLocal(returnUrl);
}
if (User.Identity.IsAuthenticated)
{
// If the current user is logged in add the new account
OAuthWebSecurity.CreateOrUpdateAccount(result.Provider, result.ProviderUserId, User.Identity.Name);
return RedirectToLocal(returnUrl);
}
else
{
// User is new, ask for their desired membership name
string loginData = OAuthWebSecurity.SerializeProviderUserId(result.Provider, result.ProviderUserId);
ViewBag.ProviderDisplayName = OAuthWebSecurity.GetOAuthClientData(result.Provider).DisplayName;
ViewBag.ReturnUrl = returnUrl;
return View("ExternalLoginConfirmation", new RegisterExternalLoginModel { UserName = result.UserName, ExternalLoginData = loginData });
}
}
and I am getting an error in this line:
if (OAuthWebSecurity.Login(result.Provider, result.ProviderUserId, createPersistentCookie: false))
Please Let me know..