Membership Provider Woes

G

Guest

I have been having some trouble with implementing a custom Membership
Provider. We have a custom data store and business logic that pulls user
information. I need some level of functionality above and beyond what the
prodiver currently allows. I need the ability to access a user id and the
user's permission id. With Forms authentication in 1.1, I would just create
a custom identiy and principal and store the information in the identity.
However, the membership provider doesn't quite support this. I can get the
login conrols to work properly with my custom membership provider, I just
can't find a way to expose more information about a user from my custom
classes. I've looked at the profile provider; I haven't had success with
that either. That doesn't seem to want to store information in an
authentication ticket like the membership provider does. Ideally I would
like everything stored in the authentication ticket (as it is with the
membership provider). I don't want to have to go to the database everytime
the page loads just to get the user id.

Sooooo...my basic requirements are as follows.
1. Use asp.net Login Controls
2. On any page, at any time, I want to know the current logged in user's id
3. On any page, at any time, I want to know the current user's permission id

So, I just want to retrieve two integer values on top of what the membership
provider gets me. I would like to avoid implementing the profile and role
provider because i feel it would be overkill. Also, I don't want to do
authentication via the session, or manually via cookies. I would like to
leverage the provider model, but I can't get it to do exactly what I need.

Can anyone help me expose this additional information without going to the
database everytime? Thanks
 
S

Steven Cheng[MSFT]

Hello Chris,

From your description, you are using the Membership/Role providers to
perform forms authentication in your ASP.NET web application. You like the
built-in membership/role provider feature and the login controls, however,
you also want to add additional custom data(specifci to each user) into the
forms authentication cookie (without querying database in each request),
correct?

Based on my experience, since ASP.NET 2.0's membershp/role provider and
service model has been well encapsulated, it is not quite convenient to
modify it or develop our own model to replace them. And what you need here
is just cache some custom data into forms auth ticket and used in other
pages later, I think you can simply add them into the
FormsAuthenticationTicket(created manually) and then access it later
through the FormsIdentity.Ticket property. e.g.

========in page which want to access the custom data in authenticatino
ticket=======
protected void Page_Load(object sender, EventArgs e)
{

//you can even define a helper function for extract custom data from
ticket.UserData string


Response.Write("<br/>CurrentIdentity: " + Context.User.Identity);

FormsIdentity identity = Context.User.Identity as FormsIdentity;

Response.Write("<br/>Ticket.UserData: " + identity.Ticket.UserData);


}
===============

the forms authentication module will help retrieve teh userdata from
ticket(from cookie) and store it in the FormsIdentity(in the
HttpContext.Current.User.Identity). Also, in your login page, you need to
manually create the FormsAuthentication ticket so that you can add
additional data into "UserData" property of the ticket. e.g.

=========in our custom logging page's code============
protected void Login1_LoggingIn(object sender, LoginCancelEventArgs e)
{
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
1,
Login1.UserName,
DateTime.Now,
DateTime.Now.AddMinutes(30),
Login1.RememberMeSet,

"some custom data want to store in ticket....", // User-data, in
this case the roles
FormsAuthentication.FormsCookiePath);


string hash = FormsAuthentication.Encrypt(ticket);
HttpCookie cookie = new HttpCookie(
FormsAuthentication.FormsCookieName,
hash);


if (ticket.IsPersistent) cookie.Expires = ticket.Expiration;


Response.Cookies.Add(cookie);

Response.Redirect(Request.QueryString["ReturnUrl"]);

}
============================

Hope this helps for your scenario.

Sincerely,

Steven Cheng

Microsoft MSDN Online Support Lead



==================================================

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



Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.

==================================================



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

Guest

Thanks Steven,

I have a few questions. The FormsIdentity class you reference, is that a
custom class I have to write that implements IIdentity or is it just
available.

Second, how do I actually store the custom information? From the looks of
it, I pass infomration into the constructor of the FormsAuthenticationTicket,
but which parameter does it? Is it the "1" that you pass in?

How can I easily access this information later? It looks like once you pass
the information into the cookie, you can pull it out using
identity.Ticket.UserData. However, isn't this just implementing a custom
IIdentity...which is kind of the asp.net 1.1 way?

Doens't the membership provider set a forms auth cookie for me
automatically? Will setting the cookie manually cause a problem?

Is there any way to intercept the data from the Membership provider? As it
stands, the membership provider will go to the database to authenticate the
user, then I will have to go to the database manually again just to get their
user information. I tried finding an event that would return the data as
with an objectdatasoure, but I haven't found any. Is there any way to avoid
this extra call that the Membership provider is doing anyway.

It's a little disappointing that I have to do so much hacking to get
something so simple to work. I would have thought .net 2.0 would have
covered this a little better.

Steven Cheng said:
Hello Chris,

From your description, you are using the Membership/Role providers to
perform forms authentication in your ASP.NET web application. You like the
built-in membership/role provider feature and the login controls, however,
you also want to add additional custom data(specifci to each user) into the
forms authentication cookie (without querying database in each request),
correct?

Based on my experience, since ASP.NET 2.0's membershp/role provider and
service model has been well encapsulated, it is not quite convenient to
modify it or develop our own model to replace them. And what you need here
is just cache some custom data into forms auth ticket and used in other
pages later, I think you can simply add them into the
FormsAuthenticationTicket(created manually) and then access it later
through the FormsIdentity.Ticket property. e.g.

========in page which want to access the custom data in authenticatino
ticket=======
protected void Page_Load(object sender, EventArgs e)
{

//you can even define a helper function for extract custom data from
ticket.UserData string


Response.Write("<br/>CurrentIdentity: " + Context.User.Identity);

FormsIdentity identity = Context.User.Identity as FormsIdentity;

Response.Write("<br/>Ticket.UserData: " + identity.Ticket.UserData);


}
===============

the forms authentication module will help retrieve teh userdata from
ticket(from cookie) and store it in the FormsIdentity(in the
HttpContext.Current.User.Identity). Also, in your login page, you need to
manually create the FormsAuthentication ticket so that you can add
additional data into "UserData" property of the ticket. e.g.

=========in our custom logging page's code============
protected void Login1_LoggingIn(object sender, LoginCancelEventArgs e)
{
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
1,
Login1.UserName,
DateTime.Now,
DateTime.Now.AddMinutes(30),
Login1.RememberMeSet,

"some custom data want to store in ticket....", // User-data, in
this case the roles
FormsAuthentication.FormsCookiePath);


string hash = FormsAuthentication.Encrypt(ticket);
HttpCookie cookie = new HttpCookie(
FormsAuthentication.FormsCookieName,
hash);


if (ticket.IsPersistent) cookie.Expires = ticket.Expiration;


Response.Cookies.Add(cookie);

Response.Redirect(Request.QueryString["ReturnUrl"]);

}
============================

Hope this helps for your scenario.

Sincerely,

Steven Cheng

Microsoft MSDN Online Support Lead



==================================================

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



Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.

==================================================



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

Steven Cheng[MSFT]

Thanks for your reply Chris,

Regarding on your new questions ,here are my understanding on this:


The FormsIdentity class you reference, is that a
custom class I have to write that implements IIdentity or is it just
available.
======================================
Sure, it is a built-in class which is used to represent security identity
in forms authentication context. This is used in both ASP.NET 1.x and 2.0
by the forms authentication Module, it will be automatically created in
each request(as long as the current user has been authenticated).


Second, how do I actually store the custom information? From the looks of
it, I pass infomration into the constructor of the
FormsAuthenticationTicket,
but which parameter does it? Is it the "1" that you pass in?
=========================================
No. "1" is the version number. It is the "some custom data want to store in
ticket...." which can be any custom data you want to store in the
authentication ticket. Custom data can only be string value, it is also
limited by the natural of cookie. You find the clear definition of the
Ticket class's constructor below:

#FormsAuthenticationTicket.FormsAuthenticationTicket(Int32, String,
DateTime, DateTime, Boolean, String, String) Constructor
http://msdn2.microsoft.com/en-us/library/kybcs83h.aspx



How can I easily access this information later? It looks like once you
pass
the information into the cookie, you can pull it out using
identity.Ticket.UserData. However, isn't this just implementing a custom
IIdentity...which is kind of the asp.net 1.1 way?
===========================================
As I have mentioned, FormsIdentity is a built-in class dedicated for
representing forms authenticated user identity, this is used from 1.x to
2.0. You can easily get this identity in each page request through
HttpContext.Current.User. Also, ASP.NET 2.0 is using the same means to
create forms authentication ticket and store it in cookie, and retrieve it
back in each request(in FormsAuthentication httpmodule). There is no
difference on these code, the only difference is that ASP.NET 2.0 have done
this for you internally and save you from coding these yourself. So what
you need to do is just add the custom string data at user's
login/authentication time and then access it through Context.Use whenever
you need it.


Doens't the membership provider set a forms auth cookie for me
automatically? Will setting the cookie manually cause a problem?
============================================
Well, this is a good question. ASP.NET 2.0 add the membership service which
can help simplify the custom security/user management. However, remember
that membership provider and forms authentication are totally separated,
you can use forms authentication without membership service(do it yourself
as ASP.NET 1.X). Or you can simply call membershp API without enabling
forms authentication.

No, "setting cookie manually" won't cause any problem, ASP.NET 2.0
FormsAuthentications class use the same code to generate the ticket and add
it into resposne cookie collection(default behavior). Here is the
diassembled code from reflector
private static HttpCookie GetAuthCookie(string userName, bool
createPersistentCookie, string strCookiePath, bool hexEncodedTicket)
{
FormsAuthentication.Initialize();
if (userName == null)
{
userName = string.Empty;
}
if ((strCookiePath == null) || (strCookiePath.Length < 1))
{
strCookiePath = FormsAuthentication.FormsCookiePath;
}
FormsAuthenticationTicket ticket1 = new FormsAuthenticationTicket(2,
userName, DateTime.Now, DateTime.Now.AddMinutes((double)
FormsAuthentication._Timeout), createPersistentCookie, string.Empty,
strCookiePath);
string text1 = FormsAuthentication.Encrypt(ticket1, hexEncodedTicket);
if ((text1 == null) || (text1.Length < 1))
{
throw new
HttpException(SR.GetString("Unable_to_encrypt_cookie_ticket"));
}
HttpCookie cookie1 = new
HttpCookie(FormsAuthentication.FormsCookieName, text1);
cookie1.HttpOnly = true;
cookie1.Path = strCookiePath;
cookie1.Secure = FormsAuthentication._RequireSSL;
if (FormsAuthentication._CookieDomain != null)
{
cookie1.Domain = FormsAuthentication._CookieDomain;
}
if (ticket1.IsPersistent)
{
cookie1.Expires = ticket1.Expiration;
}
return cookie1;
}


<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

Since you need to add custom data here, you need to manually create the
Ticket and add it into response's Cookie collection. All the API used here
are public ones, nothing incorrect.


Is there any way to intercept the data from the Membership provider? As it
stands, the membership provider will go to the database to authenticate the
user, then I will have to go to the database manually again just to get
their
user information. I tried finding an event that would return the data as
with an objectdatasoure, but I haven't found any. Is there any way to
avoid
this extra call that the Membership provider is doing anyway.
================================================
Membership API has nothing to do with forms authentication. Membershp API
just help retrieve or update the data in membership database tables. If
you're using forms authentication and want to store cached data through
forms authentication ticket, you should use forms authentication API rather
than membershp API.

If there is anything unclear, please feel free to let me know.

Sincerely,

Steven Cheng

Microsoft MSDN Online Support Lead


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

Steven Cheng[MSFT]

Hi Chris,

Thanks for your followup.

Due to caught a bad cold, I haven't had a chance to visit the newsgroup the
previous days.

For the questiosn you mentioned in the last reply:

You set the FormsAuth ticket on the Login_LoggingIn. Is
there any reason you did that instead of Login_LoggedIn? You're setting a
cookie regardless of whether the user's authentication failed or not.
===========================================

I think put the code in LoggingIn event rather than LoggedIn event is
important. This is because the Login control will automatically do the user
validation(through membership API) and generate the default authentication
ticket and add into cookie collection after the LoggingIn event before
LoggedIn event. Since we want to generate and set the authentication
ticket/cookie ourself, we should hook the code in "LoggingIn" event.

Also, I admit that the code snippet in my previous message is a bit
incomplete, the complete code logic in the LoggingIn event should look like
below:
protected void Login1_LoggingIn(object sender, LoginCancelEventArgs e)
{
//cancel it to avoid further default codelogic of login control

e.Cancel = true;

//manually use membershp API to do the authentication
if (Membership.ValidateUser(Login1.UserName, Login1.Password))
{
FormsAuthenticationTicket ticket = new
FormsAuthenticationTicket(
1,
Login1.UserName,
DateTime.Now,
DateTime.Now.AddMinutes(30),
Login1.RememberMeSet,

"some custom data want to store in ticket....", // User-data,
in this case the roles
FormsAuthentication.FormsCookiePath);


string hash = FormsAuthentication.Encrypt(ticket);
HttpCookie cookie = new HttpCookie(
FormsAuthentication.FormsCookieName,
hash);


if (ticket.IsPersistent) cookie.Expires = ticket.Expiration;


Response.Cookies.Add(cookie);

Response.Redirect(Request.QueryString["ReturnUrl"]);

}
}
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

In addition, you're welcome to post any request or feedback about the
enhancement on such feature in our product feedback center:

http://connect.microsoft.com/VisualStudio/feedback/

Thanks again for your posting.

Sincerely,

Steven Cheng

Microsoft MSDN Online Support Lead


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

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top