asp.net webforms login with custom database - asp.net

and thanks in advance for anyhelp you can provide.
My problem is i have this schema for example
table users: id, name, password, category_id | table category: id, name (with client and admin values for example (and dont want to change this schema).
I would like to know what needs to be donne in order to make a login with the default functionalities like the role authorization system of the .net framework.
I have already looked in customMembership and custom roleProvider Implementation, but i get stuck right in the login which uses neither of these two classes.
protected void LogIn(object sender, EventArgs e)
{
if (IsValid)
{
// Validate the user password
var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
var signinManager = Context.GetOwinContext().GetUserManager<ApplicationSignInManager>();
// This doen't count login failures towards account lockout
// To enable password failures to trigger lockout, change to shouldLockout: true
//fails here
var result = signinManager.PasswordSignIn(Email.Text, Password.Text, RememberMe.Checked, shouldLockout: false);
switch (result)
{
case SignInStatus.Success:
IdentityHelper.RedirectToReturnUrl(Request.QueryString["ReturnUrl"], Response);
break;
...
Do i need to implement an usermanagerProvider?

Related

ASP Identity Issue not authenticting

So I'm using ASP Identity. I customized the profile with adding isEnabled so that I know if a user is Active or not. This works fine and the database is updated without issue.
So after research, I saw that I need to override "public async override Task PasswordSignInAsync" that I can check for the isEnabled property. This works fine as well because it's passing the proper SigninStatus that I need to the Login Page.
The IdentifyConfig.cs File:
public async override Task<SignInStatus> PasswordSignInAsync(string userName, string password, bool rememberMe, bool shouldLockout)
{
var user = UserManager.FindByEmailAsync(userName).Result;
if (user.IsEnabled != false)
{
return await Task.FromResult<SignInStatus>(SignInStatus.Success);
}
return await Task.FromResult<SignInStatus>(SignInStatus.LockedOut);
}
The Login Page:
protected void LogIn(object sender, EventArgs e)
{
if (IsValid)
{
// Validate the user password
var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
var signinManager = Context.GetOwinContext().GetUserManager<ApplicationSignInManager>();
// This doen't count login failures towards account lockout
// To enable password failures to trigger lockout, change to shouldLockout: true
//var result = signinManager.PasswordSignIn(Email.Text, Password.Text, RememberMe.Checked, shouldLockout: false);
var result = signinManager.PasswordSignInAsync(Email.Text, Password.Text, RememberMe.Checked, shouldLockout: false);
switch (result.Result)
{
case SignInStatus.Success:
//IdentityHelper.RedirectToReturnUrl(Request.QueryString["ReturnUrl"], Response);
Response.Redirect("/Account/Dashboard");
break;
case SignInStatus.LockedOut:
Response.Redirect("/Account/Lockout");
break;
case SignInStatus.RequiresVerification:
Response.Redirect(String.Format("/Account/TwoFactorAuthenticationSignIn?ReturnUrl={0}&RememberMe={1}",
Request.QueryString["ReturnUrl"],
RememberMe.Checked),
true);
break;
case SignInStatus.Failure:
default:
//FailureText.Text = "Invalid login attempt";
//ErrorMessage.Visible = true;
break;
}
}
}
The switch case goes into the right case, but here's the issue. When it goes into the case SignInStatus.Success: it just stays in a loop and keeps presenting the Login Screen as if the user is not authenticated. Now, I'm sure i am missing something on the Override for
public async override Task PasswordSignInAsync(string userName, string password, bool rememberMe, bool shouldLockout)
function, but I thought if Success came back that the user is authorized.
If I uncomment
//var result = signinManager.PasswordSignIn(Email.Text, Password.Text, RememberMe.Checked, shouldLockout: false);
function it works fine, but I can't get the IsEnabled value to check. Please any advice would be appreciated on this issue.
I believe that we have a few issues that are compounding for you, making this process a bit hard to dissect
Your async implementation could cause some thread issues
We want to be sure to handle a null situation on user lookup, for a bad username
We want to be sure that we actually validate the password
We need to call the method necessary to login the user as well, as that is a responsibility of this method
I'm going to assume that you are using the non ASP.NET Core version of things. As such, this modified version should help. I have not been able to 100% validate.
public async override Task<SignInStatus> PasswordSignInAsync(string userName, string password, bool rememberMe, bool shouldLockout)
{
var user = await UserManager.FindByEmailAsync(userName);
//User not found
if (user == null)
return SignInStatus.Failure;
//User not enabled
if (user.IsEnabled != false)
return SignInStatus.Failure;
//Now, validate that the password was correct
var isValidPassword = await UserManager.CheckPasswordAsync(user, password);
if (!isValidPassword)
return SignInStatus.Failure;
//Now we know they are valid and enabled, login and return
await base.SignInAsync(user, isPersistent, false);
return SignInStatus.Success;
}
This above code SHOULD work for you, without issue. The only question is on the type that is used to pass into the CheckPassword asyc, I don't have a project ready to validate. But this should get you there.

Creating users with no password using ASP.NET Identity

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);

Getting the session ID causes the user to logout

I am observing very strange behaviour in an ASP.NET application I am writing and have reproduced the problem by creating a vanilla application from the VS2013 template and changing one line...
Background
In the LogIn event of the Login.aspx page I retrieve the sessionID from the current Session at the point the user logs in successfully. I do this in the case statement for SignInStatus.Success by adding the following line:
var sessionId = Session.SessionID;
After adding this line I can no longer log in. Typing the username and password just jumps back to the default page without showing the user as signed in.
If I remove that line, sign in works as expected.
To reproduce:
In VS2013, create a new ASP.NET Web Application. Select the Web Forms template. Change Authentication to be Individual User Accounts. Untick Host in the cloud. Untick MVC and Web API.
Once the application is created change LogIn in Login.aspx.cs to start as follows:
protected void LogIn(object sender, EventArgs e)
{
if (IsValid)
{
// Validate the user password
var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
var signinManager = Context.GetOwinContext().GetUserManager<ApplicationSignInManager>();
// This doen't count login failures towards account lockout
// To enable password failures to trigger lockout, change to shouldLockout: true
var result = signinManager.PasswordSignIn(Email.Text, Password.Text, RememberMe.Checked, shouldLockout: false);
switch (result)
{
case SignInStatus.Success:
var sessionId = Session.SessionID;
IdentityHelper.RedirectToReturnUrl(Request.QueryString["ReturnUrl"], Response);
break;
After registering a user, log out. Try and sign in again and it won't work.
If you remove the SessionId line again it will let you sign in.

ASP.NET Authentication

I have the following a login page where the user enters in their username and password.
With that info, I need to then make sure that they are part of the Admin1 role If so, I like to set a cookie on the user's machine.
With the code I have below User.InRole it doesn't enter into the if statement. If I uncomment the FormsAuthentication.SetAuthCookie(txtUserName.Text, true); above it works.
Meaning shouldn't I set the cookie only if the user is part of Admin1 role
I have the following but does not seem to work:
if (Membership.ValidateUser(txtUserName.Text, txtPassword.Text))
{
// FormsAuthentication.SetAuthCookie(txtUserName.Text, true);
if (User.IsInRole("Admin1"))
{
// code never reaches here
FormsAuthentication.SetAuthCookie(txtUserName.Text, true);
User.IsInRole("Admin1") is false right after validation, because principal object hasn't been attached to the current HttpContext yet.
If you really want to use Context.User, you need to manually attach principal object.
var username = txtUserName.Text;
var password = txtPassword.Text;
if (Membership.ValidateUser(username , password))
{
var roles = Roles.GetRolesForUser(username);
var identity = new GenericIdentity(username);
var principal = new GenericPrincipal(identity, roles);
Context.User = principal;
// Now you can use Context.User
// Basically User.IsInRole("Admin1") is same as roles.Contains("Admin1")
if (User.IsInRole("Admin1"))
{
FormsAuthentication.SetAuthCookie(username, true);
}
}
Updated - Authenticate user using Login Control
Since you are using Membership Provider and Role Provider, I would like to suggest to use Login Control.
Once user is authenticated, you can use LoggedIn event to redirect user to appropiate page.
<asp:Login ID="LoginUser" runat="server" EnableViewState="false"
RenderOuterTable="false" OnLoggedIn="LoginUser_LoggedIn">
...
</asp:Login>
protected void LoginUser_LoggedIn(object sender, EventArgs e)
{
// Now we know that user is authenticated
// Membership user = Membership.GetUser(Login1.Username);
var roles = Roles.GetRolesForUser(Login1.Username);
if(roles.Contains("Admin1"))
Response.Redirect("~/Admin/");
else
Response.Redirect("~/Users/");
}

Custom authentication module inheriting IHttpModule issue

LoginPage.aspx:-
protected void Button1_Click(object sender, EventArgs e)
{
Context.Items["Username"] = txtUserId.Text;
Context.Items["Password"] = txtPassword.Text;
//
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, Context.Items["Username"].ToString(), DateTime.Now, DateTime.Now.AddMinutes(10), true, "users", FormsAuthentication.FormsCookiePath);
// Encrypt the cookie using the machine key for secure transport
string hash = FormsAuthentication.Encrypt(ticket);
HttpCookie cookie = new HttpCookie(
FormsAuthentication.FormsCookieName, // Name of auth cookie
hash); // Hashed ticket
// Set the cookie's expiration time to the tickets expiration time
if (ticket.IsPersistent) cookie.Expires = ticket.Expiration;
Response.Cookies.Add(cookie);
Response.Redirect("Default.aspx");
}
Global.asax file:-
void Application_AuthenticateRequest(object sender, EventArgs e)
{
if (HttpContext.Current.User != null)
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
if (HttpContext.Current.User.Identity is FormsIdentity)
{
FormsIdentity id =
(FormsIdentity)HttpContext.Current.User.Identity;
FormsAuthenticationTicket ticket = id.Ticket;
// Get the stored user-data, in this case, our roles
string userData = ticket.UserData;
string[] roles = userData.Split(',');
HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(id, roles);
Response.Write(HttpContext.Current.User.Identity.Name);
Response.Redirect("Default.aspx");
}
}
}
}
I get the following error after signing in
This webpage has a redirect loop.
The webpage at http://localhost:1067/Default.aspx has resulted in too many redirects. Clearing your cookies for this site or allowing third-party cookies may fix the problem. If not, it is possibly a server configuration issue and not a problem with your computer.
This is the rough idea of what your module should look like. Your module will run on every request. You don't invoke it or pass anything to it, it just automatically fires whenever a request is made that ASP.Net is set to process.
Your module will do two things, 1) authenticate a user in the login page, 2) authenticate a user on subsequent pages. The first step is to subscribe to the BeginRequest method which will be given the current HttpApplication as the first parameter. From there you need to determine if the user is on your login page or not. If they're not on your login page, check your session or cookie or querystring token, or whatever you're using to make sure that they're still valid. If they're invalid, bounce them back to the login page.
If they're on your login page and have made a POST, look at the raw form fields and validate them. TextBoxes, checkboxes, etc don't exist here, only raw form fields. If they're valid, set your authentication token however you want (session, cookies, etc). If they're invalid, either redirect to the login page or inject a "try again" message or something.
Also, if you double-post a message please reference it so that we can follow the chain of what was already said.
class MyModule : IHttpModule
{
void IHttpModule.Init(HttpApplication context)
{
//Subscribe to the BeginRequest event
context.BeginRequest += new EventHandler(this.Application_BeginRequest);
}
private void Application_BeginRequest(Object source, EventArgs e)
{
//Initialize our variables, null checks should be put here, too
HttpApplication app = (HttpApplication)source;
HttpContext context = app.Context;
System.Web.SessionState.HttpSessionState s = context.Session;
//Normally our module needs to validate every request to make sure our request is still authenticated.
//The exception to that rule is on our logon page where they obviously don't have credentials yet.
if(!context.Request.FilePath.ToLowerInvariant().StartsWith("/login.aspx")){
//If we're here then we're not on the logon page, validate our current session according to whatever logic we want
if (s != null && s["isvalid"] == "true"){
return;
}else{
context.Response.Redirect("/login.aspx");
}
}else{
//If we're here then we're on the login page itself. If there's a post, assume that they've hit the login button
if (context.Request.HttpMethod == "POST")
{
//Whatever your form variables are called
string username = context.Request.Form["username"];
string password = context.Request.Form["password"];
//Your own validation logic would go here
if (MyCustomLogin.IsUserValid(username, password))
{
s["isvalid"] = "true";
context.Response.Redirect("/Home.aspx");
}else{
s["isvalid"] = "false";
context.Response.Redirect("/login.aspx?error=invalid_login");
}
}else{
//If we're here then the request is probably a GET or HEAD which would be from a person
//initially browsing to our page so just do nothing and pass it through normally
}
}
}
}
There is no direct way to have access to this information in the module (for authenticated user, you can access the username via the context, but not the password). The module checks if a request is carrying required authentication information and serve or deny the request based on that. Unless you deliberately from the login page collect this information and store somewhere where you can access it in the module, e.g session. But ideally, storing password is not widely recommended, collect it use it for authentication and destroy.
You might ideally throw more light on the reason why you want to have access to this information in the module and guys can then suggest methods to accomplish it.
Edited, after Chandan comment:
#Chandan, your comment here suggest to me what you want to do is use httpmodule for your authentication as against using standard form authentication. If I am on track, then you can check this project on codeproject at http://www.codeproject.com/KB/web-security/AspNetCustomAuth.aspx. Goodluck

Resources