Help with Form authentication

  • Thread starter Thread starter SAL
  • Start date Start date
S

SAL

Hello,
We are currenlty using FormAuthentication
(FormsAuthentication.SetAuthCookie) to log users into our web apps. We are
currently using the aspnetdb and the SqlMembershipProvider for role and user
management. We are using VS2008, SQL Server 2000.
My question and problem is this.
SetAuthCookie uses the username to set the authentication ticket and log the
user in. We need to expand the schema slightly and include a new field in
the database so I'm wondering if there's a way I can get the User's id from
the database and use that rather than the user name to SetAuthCookie. The
problem being that the user's username is not going to continue being unique
and the user's UserId will be.

What does SetAuthCookie user to do its magic? Is it using the
SqlMembershipProvider to do this? And, if so, do I need to implement a
custom provider to make it work slightly different?
I'm sure you can see I'm a bit confused so any help on this would be greatly
appreciated.

Thanks in advance
SAL
 
What does SetAuthCookie user to do its magic?

It is a wrapper of the security bits. If you want to see all the magic,
download Reflector from Red Gate and reflect over the method. You can
drill down and see precisely what is going on.
Is it using the
SqlMembershipProvider to do this?

The provider is how it hooks to the membership data store and is used in
the process, but the actual moving bits are in the security classes.
And, if so, do I need to implement a
custom provider to make it work slightly different?

That would be my first option, rather than rebuilding the entire
security mechanism, which is what you have to do if you completely step
outside of the box.

Overriding some methods is a pain, so you end up using Reflector to
guide your path (examing MS's code) or you Google and find a custom
provider similar to what you are trying.
I'm sure you can see I'm a bit confused so any help on this would be
greatly appreciated.

The membership bits are confusing once you step outside the box MS
provides.

Peace and Grace,


--
Gregory A. Beamer (MVP)

Twitter: @gbworld
Blog: http://gregorybeamer.spaces.live.com

*******************************************
| Think outside the box! |
*******************************************
 
Greg, thanks for you reply.
from what I can see, SetAuthCookie doesn't actually hook into the
AspNetSqlMembershipProvider. I see it reading the Authentication section of
the Web.Config file. At least I assume that's what it's doing when it makes
the: RuntimeConfig.GetAppConfig.Authentication call.
It does seem to expect an Authenticated username and in fact, you can just
pass it any old gobbledegook as a user name and it will set the cookie
accordingly apparently.
So, this kind of brings me to believe what I may really need is a custom
role provider and perhaps a custom membership provider.
The custom role provider to override the IsUserInRole method. I need to
consider one more field in this than the user's name and role.
And the custom membership provider as it looks like I may need to override
the GetUser.ProviderUserKey property so I can consider an extra field to
retrieve the key.
Does it sound like I'm on track here? Is this more work than a day or two
you think? Just asking.... :)

Thanks
SAL
 
Does it sound like I'm on track here? Is this more work than a day or
two you think? Just asking.... :)

It is not that hard to write a custom implementation. I may be able to
pull one up and post it somewhere, as I had to make it easy to have an
admin change a user's password on an app that originally used the
default SQL implementation. Only override what you need to change and
let the rest filter down if you want the easiest go of it.

I can't remember how long it took to customize ours, but I do remember
completely defining the entire problem took more time than coding the
implementation.

You do have to change the web.config file once you are done, but that is
well documented.

Peace and Grace,

--
Gregory A. Beamer (MVP)

Twitter: @gbworld
Blog: http://gregorybeamer.spaces.live.com

*******************************************
| Think outside the box! |
*******************************************
 
Thanks Greg.
That's kind of what I was thinking too. It looks like I'm, likely, going to
have to implement more than a couple of methods so that when a new user gets
added/deleted, this new field will get added too and then deleted filtering
by the new field, etc.
I haven't looked at the Membership provider implementation yet but there is
an MSDN sample for the roler provider and I think maybe for the Membership
provider too.

I bascially need the exact same functionality as that exists except that I
need this new field to be considered to add users, get the roles users are
in, get the UserId in the database etc. Once I have the UserId, well that's
the primary key anyway soooo...

I'll take a look. Thanks again.
SAL
 
Hi SAL,

I agree with Gregory.

When we use ASP.NET Login controls, ASP.NET will automatically use the
membership system to validate a user and set cookie using UserName.

If you want to use UserId as cookie key, you can create a custom Login Form
to obtain credentials from user and to validate them against a user store,
and then call FormsAuthentication.SetAuthCookie method with UserId as
parameter in codebehind class.

By far, we need to retrieve UserId from membership database. To do so, we
can create a custom SqlMembershipProvider to add new function:
GetUserID(string strUserName) .

For example, we assume that we use connection string “LocalSqlServer?and
use SQL server as database.

1.The following is custom SqlMembershipProvider class:
===============================
public class CustomMembership : SqlMembershipProvider
{
public string GetUserID(string strUserName)
{
string strUserID = "";

SqlConnection con = new
SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["L
ocalSqlServer"].ToString());
SqlCommand com = new SqlCommand("SELECT UserId FROM aspnet_Users
WHERE UserName= @username");
com.Parameters.Add("@username", SqlDbType.NVarChar).Value =
strUserName;

try
{
com.Connection = con;
com.Connection.Open();
object obj = com.ExecuteScalar();
strUserID = Convert.ToString(obj);
}
catch
{

}
finally
{
con.Close();
}


return strUserID;

}
}
===============================
2.Create Login Form page and use custom provider to retrieve UserId:
===============================
<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">

protected void btnLogin_Click(object sender, EventArgs e)
{
CustomMembership cms = (CustomMembership)Membership.Provider;

if (cms.ValidateUser(txtUserName.Text, txtPWD.Text))
{
string strUserId = cms.GetUserID(txtUserName.Text);
FormsAuthentication.SetAuthCookie(strUserId, false);
Response.Write("UserId is " + strUserId);
}
}
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>

</div>
<asp:Label ID="lblUserName" runat="server" Text="UserName"></asp:Label>
<asp:TextBox ID="txtUserName" runat="server"></asp:TextBox>
<br />
<asp:Label ID="lblPWD" runat="server" Text="PWD"></asp:Label>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<asp:TextBox ID="txtPWD" runat="server"
TextMode="Password"></asp:TextBox>
<br />
<br />
<asp:Button ID="btnLogin" runat="server" Text="Login"
onclick="btnLogin_Click" />
</form>
</body>
</html>
===============================
3.Specify custom provider as Membership provider in web.config:
===============================
<membership defaultProvider="CustomAspNetSqlMembershipProvider">

<providers>

<add

name="CustomAspNetSqlMembershipProvider"

type="CustomMembership"

connectionStringName="LocalSqlServer"

enablePasswordRetrieval="false"

enablePasswordReset="true"

requiresQuestionAndAnswer="true"

applicationName="/"

requiresUniqueEmail="false"

/>

</providers>

</membership>
===============================

For more information, you can refer to implementing a Membership Provider:
http://msdn.microsoft.com/en-us/library/f1kyba5e.aspx


I look forward to receiving your test results.



Best Regards,
Thomas Sun

Microsoft Online Partner Support

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

With newsgroups, MSDN subscribers enjoy unlimited, free support as opposed
to the limited number of phone-based technical support incidents. Complex
issues or server-down situations are not recommended for the newsgroups.
Issues of this nature are best handled working with a Microsoft Support
Engineer using one of your phone-based incidents.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
 
Thomas,
thank you for the code samples and that doesn't look too bad at all to
implement. I will try to do some testing by tomorrow to see how that goes
and let you know.

SAL


Thomas Sun said:
Hi SAL,

I agree with Gregory.

When we use ASP.NET Login controls, ASP.NET will automatically use the
membership system to validate a user and set cookie using UserName.

If you want to use UserId as cookie key, you can create a custom Login
Form
to obtain credentials from user and to validate them against a user store,
and then call FormsAuthentication.SetAuthCookie method with UserId as
parameter in codebehind class.

By far, we need to retrieve UserId from membership database. To do so, we
can create a custom SqlMembershipProvider to add new function:
GetUserID(string strUserName) .

For example, we assume that we use connection string "LocalSqlServer?and
use SQL server as database.

1.The following is custom SqlMembershipProvider class:
===============================
public class CustomMembership : SqlMembershipProvider
{
public string GetUserID(string strUserName)
{
string strUserID = "";

SqlConnection con = new
SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["L
ocalSqlServer"].ToString());
SqlCommand com = new SqlCommand("SELECT UserId FROM aspnet_Users
WHERE UserName= @UserName");
com.Parameters.Add("@UserName", SqlDbType.NVarChar).Value =
strUserName;

try
{
com.Connection = con;
com.Connection.Open();
object obj = com.ExecuteScalar();
strUserID = Convert.ToString(obj);
}
catch
{

}
finally
{
con.Close();
}


return strUserID;

}
}
===============================
2.Create Login Form page and use custom provider to retrieve UserId:
===============================
<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">

protected void btnLogin_Click(object sender, EventArgs e)
{
CustomMembership cms = (CustomMembership)Membership.Provider;

if (cms.ValidateUser(txtUserName.Text, txtPWD.Text))
{
string strUserId = cms.GetUserID(txtUserName.Text);
FormsAuthentication.SetAuthCookie(strUserId, false);
Response.Write("UserId is " + strUserId);
}
}
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>

</div>
<asp:Label ID="lblUserName" runat="server" Text="UserName"></asp:Label>
<asp:TextBox ID="txtUserName" runat="server"></asp:TextBox>
<br />
<asp:Label ID="lblPWD" runat="server" Text="PWD"></asp:Label>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<asp:TextBox ID="txtPWD" runat="server"
TextMode="Password"></asp:TextBox>
<br />
<br />
<asp:Button ID="btnLogin" runat="server" Text="Login"
onclick="btnLogin_Click" />
</form>
</body>
</html>
===============================
3.Specify custom provider as Membership provider in web.config:
===============================
<membership defaultProvider="CustomAspNetSqlMembershipProvider">

<providers>

<add

name="CustomAspNetSqlMembershipProvider"

type="CustomMembership"

connectionStringName="LocalSqlServer"

enablePasswordRetrieval="false"

enablePasswordReset="true"

requiresQuestionAndAnswer="true"

applicationName="/"

requiresUniqueEmail="false"

/>

</providers>

</membership>
===============================

For more information, you can refer to implementing a Membership Provider:
http://msdn.microsoft.com/en-us/library/f1kyba5e.aspx


I look forward to receiving your test results.



Best Regards,
Thomas Sun

Microsoft Online Partner Support

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

With newsgroups, MSDN subscribers enjoy unlimited, free support as opposed
to the limited number of phone-based technical support incidents. Complex
issues or server-down situations are not recommended for the newsgroups.
Issues of this nature are best handled working with a Microsoft Support
Engineer using one of your phone-based incidents.
==================================================

This posting is provided "AS IS" with no warranties, and confers no
rights.
 
Hi SAL,

Thanks for your response.

If you have any question, please feel free to let me know.


Best Regards,
Thomas Sun

Microsoft Online Partner Support

--------------------

|
| Thomas,
| thank you for the code samples and that doesn't look too bad at all to
| implement. I will try to do some testing by tomorrow to see how that goes
| and let you know.
|
| SAL
|
|
 
Thomas,
it appears we can't go this way after all because of an earlier decision we
made to put all users under one application name and hence all applications.
This was because management didn't want to have to re-enter users for each
application. And, since the aspnet_Users table has a unique constraint on
the ApplicationId/UserName fields, we can put duplicate usernames in the
database. So, we either need to step back and re-think the thing or redo the
whole database. So, we're stepping back.

Thanks again for your code samples. I may wind up using them after all.

S

Thomas Sun said:
Hi SAL,

I agree with Gregory.

When we use ASP.NET Login controls, ASP.NET will automatically use the
membership system to validate a user and set cookie using UserName.

If you want to use UserId as cookie key, you can create a custom Login
Form
to obtain credentials from user and to validate them against a user store,
and then call FormsAuthentication.SetAuthCookie method with UserId as
parameter in codebehind class.

By far, we need to retrieve UserId from membership database. To do so, we
can create a custom SqlMembershipProvider to add new function:
GetUserID(string strUserName) .

For example, we assume that we use connection string "LocalSqlServer?and
use SQL server as database.

1.The following is custom SqlMembershipProvider class:
===============================
public class CustomMembership : SqlMembershipProvider
{
public string GetUserID(string strUserName)
{
string strUserID = "";

SqlConnection con = new
SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["L
ocalSqlServer"].ToString());
SqlCommand com = new SqlCommand("SELECT UserId FROM aspnet_Users
WHERE UserName= @UserName");
com.Parameters.Add("@UserName", SqlDbType.NVarChar).Value =
strUserName;

try
{
com.Connection = con;
com.Connection.Open();
object obj = com.ExecuteScalar();
strUserID = Convert.ToString(obj);
}
catch
{

}
finally
{
con.Close();
}


return strUserID;

}
}
===============================
2.Create Login Form page and use custom provider to retrieve UserId:
===============================
<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">

protected void btnLogin_Click(object sender, EventArgs e)
{
CustomMembership cms = (CustomMembership)Membership.Provider;

if (cms.ValidateUser(txtUserName.Text, txtPWD.Text))
{
string strUserId = cms.GetUserID(txtUserName.Text);
FormsAuthentication.SetAuthCookie(strUserId, false);
Response.Write("UserId is " + strUserId);
}
}
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>

</div>
<asp:Label ID="lblUserName" runat="server" Text="UserName"></asp:Label>
<asp:TextBox ID="txtUserName" runat="server"></asp:TextBox>
<br />
<asp:Label ID="lblPWD" runat="server" Text="PWD"></asp:Label>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<asp:TextBox ID="txtPWD" runat="server"
TextMode="Password"></asp:TextBox>
<br />
<br />
<asp:Button ID="btnLogin" runat="server" Text="Login"
onclick="btnLogin_Click" />
</form>
</body>
</html>
===============================
3.Specify custom provider as Membership provider in web.config:
===============================
<membership defaultProvider="CustomAspNetSqlMembershipProvider">

<providers>

<add

name="CustomAspNetSqlMembershipProvider"

type="CustomMembership"

connectionStringName="LocalSqlServer"

enablePasswordRetrieval="false"

enablePasswordReset="true"

requiresQuestionAndAnswer="true"

applicationName="/"

requiresUniqueEmail="false"

/>

</providers>

</membership>
===============================

For more information, you can refer to implementing a Membership Provider:
http://msdn.microsoft.com/en-us/library/f1kyba5e.aspx


I look forward to receiving your test results.



Best Regards,
Thomas Sun

Microsoft Online Partner Support

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

With newsgroups, MSDN subscribers enjoy unlimited, free support as opposed
to the limited number of phone-based technical support incidents. Complex
issues or server-down situations are not recommended for the newsgroups.
Issues of this nature are best handled working with a Microsoft Support
Engineer using one of your phone-based incidents.
==================================================

This posting is provided "AS IS" with no warranties, and confers no
rights.
 
Hi SAL,

Thanks for your response.

We can store information for multiple applications in a single database
without duplicate user names, and multiple ASP.NET applications can use the
same user database by specifying the same value in the applicationName
attribute of SqlMembershipProvider Configuration in web.config.

To implement single login for multiple ASP.NET application using Forms
Authentication, we need to make sure the validationKey and decryptionKey
values in <machineKey> element of these ASP.NET applications are the same.
Besides, we also need to ensure the name and path attributes in the <forms>
element is same for each application.

For more information about SqlMembershipProvider Configuration attribute,
see
http://msdn.microsoft.com/en-us/library/ms998347.aspx

For more information about How do I implement single sign on using forms
authentication, see
http://msdn.microsoft.com/en-us/library/bb981440.aspx#_How_do_I_7


I look forward to receiving your test results.



Best Regards,
Thomas Sun

Microsoft Online Partner Support
--------------------
|
| Thomas,
| it appears we can't go this way after all because of an earlier decision
we
| made to put all users under one application name and hence all
applications.
| This was because management didn't want to have to re-enter users for
each
| application. And, since the aspnet_Users table has a unique constraint on
| the ApplicationId/UserName fields, we can put duplicate usernames in the
| database. So, we either need to step back and re-think the thing or redo
the
| whole database. So, we're stepping back.
|
| Thanks again for your code samples. I may wind up using them after all.
|
| S
|
 
Back
Top