How can i validate password for user login - asp.net

I have the following code to check for valid users:
protected void Login_LoginError(object sender, EventArgs e) {
//See if this user exists in the database
MembershipUser userinfo = Membership.GetUser(Login.UserName);
if (userinfo == null || !userinfo.IsApproved || userinfo.IsLockedOut) {
//The user entered an invalid username/password...
Login.FailureText = "Invalid User/Password";
} else {
Login.FailureText = string.Empty;
}
}
This code doesn't show the failure text when the password is wrong for users, I need code-behind logic for validating both the user and password! Any suggestions appreciated.

You should use the Authenticate handler on the login control. For more reading see this link
If you're using the out of the box MembershipProvider you can authenticate your user with the Membership.ValidateUser method in the Authenticate handler.

Related

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/");
}

Reset Password Strategy

I have a custom Register form which includes a security question and answer - works fine.
However, I have the following reset password logic which only works if the requiresQuestionAndAnswer property is set to false in my Web.Config file. Can someone show me a recommended way to code the question and answer logic into my ResetPassword code-behind? Is another trip to the DB necessary here?
public void ResetPassword_OnClick(object sender, EventArgs args)
{
string newPassword;
u = Membership.GetUser(UsernameTextBox.Text, false);
newPassword = u.ResetPassword();
if (newPassword != null)
{
Msg.Text
= "Password reset. Your new password is: "
+ Server.HtmlEncode(newPassword);
}
else
{
Msg.Text
= "Password reset failed. Please re-enter your values.";
}
}
I found the answer here:
MembershipUser.PasswordQuestion Property
"If RequiresQuestionAndAnswer is true, then the password answer for a membership user must be supplied to the GetPassword and ResetPassword methods."

ASP.NET membership - Redirect users with unpaid invoice

I'm working on a custom implementation of ASP.NET membership, which uses my own database tables. Everything works as it should, but I need to redirect customers, which have not paid their invoice, to a payment page. This should not only happen on login, but also for users which already are logged in, so if an invoice is registered as "not paid" while the user is logged in, then the user must be redirected to the payment page, the next time they load a page.
Can this be done?
I did something similar to this using a HttpModule. What you want to do is handle the PreRequest event and if they are authenticated and if so make your unpaid invoice check and redirect as necessary.
e.g.
protected void OnPreRequestHandlerExecute(object sender, EventArgs e)
{
if (HttpContext.Current.Request.Path != "/UnPaid.aspx" && HttpContext.Current.User != null)
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
// You don't want to make this check for every resource type
bool isPage = HttpContext.Current.Request.Path.EndsWith(".aspx") || HttpContext.Current.Request.Path == "/";
if (isPage)
{
bool isPaid = false; // Make isPaid check here
if (!isPaid)
{
// Optional pass ina return url for after the invoice is paid
string returnUrl = HttpUtility.UrlEncode(HttpContext.Current.Request.RawUrl, HttpContext.Current.Request.ContentEncoding);
HttpContext.Current.Response.Redirect(string.Concat("/UnPaid.aspx?ReturnUrl=", returnUrl), true);
}
}
}
}
}
I wouldn't let the membership provider know this information. It is job of your application to know this, not your security model. It may be as simple as adding/removing a role, but that's not ideal either.
You can do that on global.asax using the Application_AuthenticateRequest
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
string cTheFile = HttpContext.Current.Request.Path;
if(!cTheFile.EndsWith("ThePaymentPage.aspx"))
{
if(HttpContext.Current.User != null
&& HttpContext.Current.User.Identity != null
&& HttpContext.Current.User.Identity.IsAuthenticated)
{
// check here if need to redirect or not.
if(NeedPayment())
{
HttpContext.Current.Responce.Redirect("ThePaymentPage.aspx");
}
}
}
}
This is called on every page, so maybe you can add some more checks and make it real fast. Other checks can be if the page ends on .aspx
How about having inheritance.
You can "inject" a BasePage between Page class and your ASPX code behind class.
This way, you will have access to your business logic classes and then you can decide, on each request, where the user should be re-directed.
Me too agree on the point that this logic should be handled by your applications business logic instead of the security model.
Hope this helps.

how to prevent the user repeat-login

In my application,I do not want two user login with the same login name.
For example, user1 login with name "test1",then user2 try to login with "test1" too,but at this moment the user1's formauthentication does not expire,so the login of user2 should be denied.
I created a cache class to record all the active session:
public class SessionDb {
private static Dictionary<string, HttpSessionState> sessionDB=new Dictionary<string, HttpSessionState>();
public SessionDb() {
}
public static void addUserAndSession(string name, HttpSessionState session) {
sessionDB.Add(name, session);
}
public static bool containUser(string name) {
//in fact,here I also want to check if this session is active or not ,but I do not find the method like session.isActive() or session.HasExpire() or something else.
return sessionDB.ContainsKey(name);
}
public static void removeUser(string name) {
if(sessionDB.ContainsKey(name)) {
sessionDB.Remove(name);
}
}
}
In the login.aspx.cs:
//check the name and password
if(checkNameAndPass(sUserName, sUserPwd)) {
if(!SessionDb.containUser(sUserName)) {
//let the user login
Session["current_user_name"] = sUserName;
SessionDb.addUserAndSession(sUserName, Session);
FormsAuthentication.RedirectFromLoginPage(UserName.Text, false);
}
else {
//
this.error.Text=string.Format("user {0} have logined!", sUserName);
}
}
Global.asax:
void Session_End(object sender, EventArgs e)
{
SessionDb.removeUser(Session["current_user_name"].ToString());
}
But it seems that it the Session_End() method is called at some time according the timeout setting in the sessionState.
Obviously I need the the SessionDb remove the related session when the authentication timeout.
Any idea to improve my code? or any other idea to implement my requirement?
I just do not want the user repeat-login(with the same name).
UPDATE:
BTW,I think my code also have some problems: I store the log in token using the Session,but how about if the formauthentication have timeout but the session does not?
If you are using a Membership provider:
A user is considered online if the current date and time minus the UserIsOnlineTimeWindow property value is earlier than the LastActivityDate for the user.
from MSDN Article
so you can simple use the check
Membership.IsOnline();
before you login the user.
In asp.net site how to prevent multiple logins of same user id?
Another approach (Disclaimer: I have not tested this.):
In the web.config add userIsOnlineTimeWindow to 1 and in the loggingIn event handler:
protected void Login1_LoggingIn(object sender, LoginCancelEventArgs e)
{
MembershipUser u = Membership.GetUser(Login1.UserName);
Response.Write(u.IsOnline);
if (u.IsOnline)
{
Login1.FailureText = "A user with this username is already logged in.";
e.Cancel = true;
}

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