If you are using asp.net forms authentication it already provides the mechanisms for a user to reset a password by using a security question. This can be configured in the Web.config and used with a Password Recovery control, part of ASP.net login controls.
Setting in web.config under your membership providers section:
requiresQuestionAndAnswer - When set to true the Question view will be required for the user's password to be retrieved or reset. When set to false the Question view is not displayed to the user.
Using the Password Recovery control here is sample code:
<asp:PasswordRecovery ID="PasswordRecovery1" runat="server">
<QuestionTemplate>
<h2>Forgot Password</h2>
Hello <asp:Literal ID="UserName" runat="server"></asp:Literal><br />
Please answer your password question : <br />
<asp:Literal ID="Question" runat="server"></asp:Literal>
<asp:TextBox ID="Answer" runat="server"></asp:TextBox><br />
<asp:Button ID="SubmitButton" runat="server" Text="Send Answer By Mail"
CommandName="Submit"/><br />
<asp:Literal ID="FailureText" runat="server"></asp:Literal>
</QuestionTemplate>
</asp:PasswordRecovery>
For more information on using the ASP.net Login controls go to http://quickstarts.asp.net/QuickStartv20/aspnet/doc/ctrlref/login/default.aspx
As far as not sending the email, you can cancel the email by adding OnSendingMail="CancelEmail" in the Password recovery control and then add code behind like below and then just display the new reset password on the screen.
Sub CancelEmail(ByVal sender As Object, ByVal e As MailMessageEventArgs)
e.Cancel = True
End Sub
Hope that helps!
Impersonating a user using Forms Authentication can be done. The following code does work.
The Visual Studio Magazine article referred to by Robert is an excellent resource. There are a some issues with the example code in the article, so I've included some working code below.
Note: If you are using Visual Studio, make sure to launch it "Run as Administrator" to avoid problems with UAC blocking impersonation.
// in your login page (hook up to OnAuthenticate event)
protected void LoginControl_Authenticate(object sender, AuthenticateEventArgs e)
{
int token;
// replace "YOURDOMAIN" with your actual domain name
e.Authenticated = LogonUser(LoginUser.UserName,"YOURDOMAIN",LoginUser.Password,8,0,out token);
if (e.Authenticated) {
Session.Add("principal", new WindowsPrincipal(new WindowsIdentity(new IntPtr(token))));
}
}
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword,
int dwLogonType, int dwLogonProvider, out int TokenHandle);
// in global.asax.cs
void Application_PreRequestHandlerExecute(object send, EventArgs e)
{
if (Thread.CurrentPrincipal.Identity.IsAuthenticated == true && HttpContext.Current.Session != null) {
WindowsPrincipal windowsPrincipal = (WindowsPrincipal)Session["principal"];
Session["principal"] = (GenericPrincipal)Thread.CurrentPrincipal;
Thread.CurrentPrincipal = windowsPrincipal;
HttpContext.Current.User = windowsPrincipal;
HttpContext.Current.Items["identity"] = ((WindowsIdentity)windowsPrincipal.Identity).Impersonate();
}
}
// in global.asax.cs
void Application_PostRequestHandlerExecute(object send, EventArgs e)
{
if (HttpContext.Current.Session != null && Session["principal"] as GenericPrincipal != null) {
GenericPrincipal genericPrincipal = (GenericPrincipal)Session["principal"];
Session["principal"] = (WindowsPrincipal)Thread.CurrentPrincipal;
Thread.CurrentPrincipal = genericPrincipal;
HttpContext.Current.User = genericPrincipal;
((WindowsImpersonationContext)HttpContext.Current.Items["identity"]).Undo();
}
}
// test that impersonation is working (add this and an Asp:Label to a test page)
protected void Page_Load(object sender, EventArgs e)
{
try {
// replace YOURSERVER and YOURDB with your actual server and database names
string connstring = "data source=YOURSERVER;initial catalog=YOURDB;integrated security=True";
using (SqlConnection conn = new SqlConnection(connstring)) {
conn.Open();
SqlCommand cmd = new SqlCommand("SELECT SUSER_NAME()", conn);
using (SqlDataReader rdr = cmd.ExecuteReader()) {
rdr.Read();
Label1.Text = "SUSER_NAME() = " + rdr.GetString(0);
}
}
}
catch {
}
}
Update:
You should also handle Application_EndRequest
, because calls like Response.End()
will bypass Application_PostRequestHandlerExecute
.
Another issue is that the WindowsIdentity may get garbage collected, so you should create a new WindowsIdentity and WindowsPrincipal from the logon token on every request.
Update2:
I'm not sure why this is getting downvoted, because it works. I've added the pinvoke signature and some test code. Again, launch Visual Studio using "Run as Administrator". Google how to do that if you don't know how.
Best Answer
I would not set IsActive = false. That is for disabling an account, preventing any type of login. Instead, I do not see a problem leaving them with the ability to login. I would use a Membership Role to specify when a user is verified. I.e. "Verified".
If they have not activated their account and attempt to login, you simply check to see if they have the Verified role. If not, redirect them to a page of "You account is not yet activated. Please wait for snail mail". Or a "Please enter your verification code below." message.
When they do finally enter the proper verification code from snail mail, you simply add them to the Verified role.
This gives you the control of allowing them to login and "check" the status of their account. And, resolves your IsActive issue.