I am a PHP guy but am in the process of making a log-in page in ASP.NET MVC4. I am expecting to store the ID, Username and Roles of the user in session. So far what I am doing is as follows. If I am correct it saves the cookie with the username.
[HttpPost]
public ActionResult Login(Models.UserLoginModel user)
{
if (ModelState.IsValid)
{
Models.User u = new Models.User();
if (u.IsValid(user.Username, user.Password))
{
FormsAuthentication.SetAuthCookie(user.Username, user.RememberMe);
return RedirectToAction("Index", "Accounts");
}
else
{
ModelState.AddModelError("", "Login data is incorrect!");
}
}
return View(user);
}
My interest is to store more information and control validation time. I was advised and asked to use FormAuthenticationTicket class. I replaced FormsAuthentication.SetAuthCookie(user.Username, user.RememberMe); with
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket
(
1,
user.Username,
DateTime.Now,
DateTime.Now.AddMinutes(30),
false,
"Some User Data",
FormsAuthentication.FormsCookiePath
);
Response.Cookies.Add
(
new HttpCookie
(
FormsAuthentication.FormsCookieName,
FormsAuthentication.Encrypt(ticket)
)
);
It looks cool, I didn't test it though, has flexibility. But problem is how I could receive this information.
How can I get these information back and determine if the user is logged in and also other necessary information saved inside the FormsAuthenticationTicket.
Thanks in advance.
Like you would any ticket:
var cookie = Request.Cookies[FormsAuthentication.FormsCookieName];
var ticketInfo = FormsAuthentication.Decrypt(cookie.Value);
Since it's a security ticket, if you don't need to access the information from client JavaScript, also set HttpOnly to true. This means the cookie is only accessible on the server.
Related
I have created the code below to enable authentication on my ASP.NET CORE 3.1 site that is not using full blown Identity framework but instead just UseAuthentication in startup.
Startup.cs cookie settings:
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(cookieOptions => {
cookieOptions.Cookie.Name = "UserLoginCookie";
cookieOptions.LoginPath = "/Login/";
cookieOptions.ExpireTimeSpan = TimeSpan.FromMinutes(1);
cookieOptions.SlidingExpiration = true;
});
services.AddRazorPages().AddRazorPagesOptions(options => {
options.Conventions.AllowAnonymousToFolder("/Login");
options.Conventions.AuthorizeFolder("/");
});
The indexmodel for my razor login page utilized the following cookie related code:
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, UserName)
};
var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
if (RememberMe)
{
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity),
new AuthenticationProperties
{
IsPersistent = RememberMe,
ExpiresUtc = DateTimeOffset.UtcNow.AddDays(30)
});
}
else
{
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity));
}
return RedirectToPage("/index");
I allow the user to select remember me or not on the login page and if they do then it creates a persistent cookie. Therefore, if the user selects this option and leaves the site they are not required to login in again as long as the cookie has not expired. My question is can a cookie only store the username such that if the user leaves the site/closes the browser they must login again but the username is stored in the login form from the cookie? If this is possible is there a secure way to implement this using a hash and salt with the username stored in the cookie?
If this is not possible does the current method I am using for authentication securely hash the username and password by default or do I need to do some additional settings for this? When i look into the browser application tab for the cookies it looks to be hashed but not sure what sure to what extent the hash is being secured with. Any insight on this would be great.
I have been given the requirement to provide the ability to create users through the UI with no password. I am trying to accomplish this using ASP.NET Identity.
I am able to successfully create a user without a password using the UserManager's Create method:
if (vm.ShouldHavePassword)
{
userManager.Create(userToInsert, vm.Password);
}
else
{
userManager.Create(userToInsert);
}
After the call to the Create method, the test user gets successfully saved into our AspNetUsers table. And when I do not provide a password, the PasswordHash column in our AspNetUsers table is set to NULL.
My issue is, I cannot login as the test user that does not have a password. The following is the method call that we use to validate a user's credentials:
result = await SignInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, shouldLockout: false);
I attempted to login as a test user that has a NULL PasswordHash multiple times. To do this, I do not provide a password in our login form. As a result, a NULL password is passed into the PasswordSignInAsync method. The return value of this method call is always SignInStatus.Failure.
Using ASP.NET Identity, how can I configure my code to correctly authenticate user credentials when the credentials contain a NULL password, and the user in the database contains a NULL PasswordHash? Is such a thing even possible?
Yes you can. ASP.NET Identity Framework is fully customizable. Just override PasswordValidator.ValidateAsync and PasswordHasher.VerifyHashedPassword methods like this:
internal class CustomPasswordValidator: PasswordValidator
{
public override async Task<IdentityResult> ValidateAsync(string item)
{
if (string.IsNullOrEmpty(item)) return IdentityResult.Success;
return await base.ValidateAsync(item);
}
}
internal class CustomPasswordHasher : PasswordHasher
{
public override PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword)
{
if (hashedPassword == null && string.IsNullOrEmpty(providedPassword))
return PasswordVerificationResult.Success;
return base.VerifyHashedPassword(hashedPassword, providedPassword);
}
}
And set them like this:
var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
manager.PasswordValidator = new CustomPasswordValidator();
manager.PasswordHasher = new CustomPasswordHasher();
Okay, what you need to do is find the user (AspNetUsers user) using your db context. After you have the user, you can check if their PasswordHash is null.
If yes, then just sign them in using SignInManager.SignIn.
If not, use SignInManager.PasswordSignIn.
example..
//alternatively, you can find the user using Email, Id or some other unique field
var user = db.AspNetUsers.FirstOrDefault(p => p.UserName);
if (user != null)
{
if (user.PasswordHash == null)
await SignInManager.SignInAsync(user, true, true);
else
await SignInManager.PasswordSignInAsync(model.UserName, model.Password,
model.RememberMe, shouldLockout: false);
}
Hope it helps.
I don't think you can validate user without password. As a workaround: Instead of blank password, I'll recommend to use some Dummy/Common password from C# code, both while creating User and while validating credential
When creating user
if (vm.ShouldHavePassword)
{
userManager.Create(userToInsert, vm.Password);
}
else
{
userManager.Create(userToInsert, "someDummy123$");
}
When validating
result = await SignInManager.PasswordSignInAsync(model.UserName, "someDummy123$", model.RememberMe, shouldLockout: false);
How can i create a cookie step by step,
that stores the user login id and password when he/she clicks Remember Me? option
and i am planing to kill this cookie after certain time
Cookies are created the same way as they are in plain old ASP.NET, you just need to access the Response.
public ActionResult Login(string username, string password, bool rememberMe)
{
// validate username/password
if (rememberMe)
{
HttpCookie cookie = new HttpCookie("RememberUsername", username);
Response.Cookies.Add(cookie);
}
return View();
}
However, if you're using Forms Auth, you can just make your FormsAuth ticket cookie persistent:
public ActionResult Login(string username, string password, bool rememberMe)
{
// validate username/password
FormsAuthentication.SetAuthCookie(username, rememberMe);
return View();
}
You can read cookies like this:
public ActionResult Index()
{
var cookie = Request.Cookies["RememberUsername"];
var username = cookie == null ? string.Empty : cookie.Value; // if the cookie is not present, 'cookie' will be null. I set the 'username' variable to an empty string if its missing; otherwise i use the cookie value
// do what you wish with the cookie value
return View();
}
If you are using Forms Authentication and the user is logged in, you can access their username like this:
public ActionResult Index()
{
var username = User.Identity.IsAuthenticated ? User.Identity.Name : string.Empty;
// do what you wish with user name
return View();
}
It is possible to decrypt and read the contents of a ticket. You can even store small amounts of custom data in the ticket, if you need to. See this article for more info.
I implemented custom authentication/authorization based on this tutorial http://www.mattwrock.com/post/2009/10/14/Implementing-custom-Membership-Provider-and-Role-Provider-for-Authinticating-ASPNET-MVC-Applications.aspx
It works fine. I implemented it because I don't want to have stored procedures in my database and possibility to use different RDBMS.
But I have one issue here. I authenticate user but I don't know how to store UserId somewhere so when I need to get something from database based on UserID to get it. Something like:
List<Product> products = productsRepository.GetProductsByUserId(User.UserID);
How to make this?
BTW Is there any better way to make custom authentication/authorization than this from this tutorial?
Thanks
If you've actually implemented all the methods, and you're populating the built-in MembershipUser, then simply Membership.GetUser().ProviderUserKey will return ther UserId.
in my solution I use
Docent docent = DocentRepo.GetByID(User.Identity.Name);
maybe this can be of use to you
If you're using FormsAuthentification you can encode some custom user data in your cookie / ticket besides UserName. But you have to manually create a FormsAuthenticationTicket and set UserData property to the user's id during login. This way you can have both UserName & UserId.
// during login
var authCookie = FormsAuthentication.GetAuthCookie(userName, createPersistentCookie);
var ticket = FormsAuthentication.Decrypt(authCookie.Value);
// preserve data in your configuration
var ticketWithId = new FormsAuthenticationTicket(
version: ticket.Version,
name: ticket.Name,
issueDate: ticket.IssueDate,
expiration: ticket.Expiration,
isPersistent: ticket.IsPersistent,
userData: userId);
authCookie.Value = FormsAuthentication.Encrypt(ticketWithId);
_context.Response.Cookies.Add(authCookie);
Then you can have an extension method for Controller or HttpContext classes:
public int? GetUserId(this Controller controller) {
var identity = (FormsIdentity)controller.User.Identity;
int id;
if (int.TryParse(identity.Ticket.UserData, out id))
return id;
return null;
}
But if you don't need both UserId & UserName data for your user, than HttpContext.User.Identity.Name or Controller.User.Identity.Name will have the username for your current user
My ASP.NET MVC web application allows administrators to change their own, or other users' usernames.
Users are logged in by calling FormsAuthentication.SetAuthCookie(userName [string], createPersistentCookie [bool]). They are logged out by calling FormsAuthentication.SignOut(). I understand that after updating the username I'd need to sign them out and back in again. But how do I retrieve the existing value of createPersistentCookie? e.g. how do I retain their original 'remember me' setting when signing them back in?
var cookieName = FormsAuthentication.FormsCookieName;
var request = HttpContext.Current.Request;
var cookie = request.Cookies.Get(cookieName);
if (cookie == null)
return;
try
{
var ticket = FormsAuthentication.Decrypt(cookie.Value);
//This should give you what you want...
bool isPersistent = ticket.IsPersistent;
}
catch (Exception ex)
{
//Logging
}