Implementing a Custom Membership Provider

J

Jonathan Wood

Although this will be a challenge at my level of ASP.NET knowledge, I'm
thinking I should implement my own membership provider class.

Looking over the methods I must implement, a number of questions come to
mind.

1. How would one implement GetNumberOfUsersOnline? I'm not sure where there
is any indication of this? And it this affected by the "Remember me next
time" checkbox, which doesn't seem to work like it does on any other site
I've seen.

2. If I want to be able to provide a user password in response to the user
answering their private question, how would I best store the password? I
understand the default encryption cannot be unencrypted, but suspect that no
encryption at all is not the best approach.

3. I see that the ASP.NET SQL membership provider uses uniqueidentifier for
the primary key for each user. Is there any particular reason to use this
type instead of an automatically incrementing integer? Any downside to using
integers?

I'd appreciate any tips with respect to any of these issues.

Thanks!
 
R

Registered User

Although this will be a challenge at my level of ASP.NET knowledge, I'm
thinking I should implement my own membership provider class.

Looking over the methods I must implement, a number of questions come to
mind.

1. How would one implement GetNumberOfUsersOnline? I'm not sure where there
is any indication of this? And it this affected by the "Remember me next
time" checkbox, which doesn't seem to work like it does on any other site
I've seen.
I just looked at some 1.1stuff I wrote and GetNumberOfUsersOnline
method throws a NotSupportedException. At best the return value will
be a snapshot of an estimate, just a meaningless number. There was
just no reason to implement the method.
2. If I want to be able to provide a user password in response to the user
answering their private question, how would I best store the password? I
understand the default encryption cannot be unencrypted, but suspect that no
encryption at all is not the best approach.
There is nothing wrong with not being able to decrypt a stored
password. When a user makes the lost password request the server can
create a new password, encrypt and store a copy before sending the
unencrypted password to the user. Once the user logs in using the new
password, they can change the password to what ever they desire.
3. I see that the ASP.NET SQL membership provider uses uniqueidentifier for
the primary key for each user. Is there any particular reason to use this
type instead of an automatically incrementing integer? Any downside to using
integers?
I believe GUIDs are preferable because they can have meaning across
tables, data stores etc. When one account can have multiple
memberships, the different memberships can use the same membership ID.

regards
A.G.
 
S

sloan

Here are some tidibts, not meant to be a complete answer:



1. I think you need to write some code to keep tracked of
"LastActivityDate", and some rule that says anybody active in the last (20?)
minutes is "online".
Something like that.

2. That is up to you....one consideration is if you want the user to be
able to recover or reset their lost password.

3. I now prefer guid's over ints for primary keys. It allows you (one
thing) to build relationships OUTSIDE of the database, because you're not
waiting on a return value for the SCOPE_IDENTITY. Not that this is a
compelling reason for a MembershipProvider.
There are pros and cons of guids/uniqueidentifiers. I guess as some level,
you just gotta pick one. And MS picked uniqueidentifiers.


http://imar.spaanjaars.com/QuickDocId.aspx?quickdoc=404
msdn.microsoft.com/asp.net/downloads/providers/

Coding up and seeing what others have done (including this Access version)
is a good exercise. I have yet to use Access as the Membership datastore,
but I learnt alot going through the tutorial and getting it to work.
 
J

Jonathan Wood

Registered User,
I just looked at some 1.1stuff I wrote and GetNumberOfUsersOnline
method throws a NotSupportedException. At best the return value will
be a snapshot of an estimate, just a meaningless number. There was
just no reason to implement the method.

I understand, but it would be useful for my application.
There is nothing wrong with not being able to decrypt a stored
password. When a user makes the lost password request the server can
create a new password, encrypt and store a copy before sending the
unencrypted password to the user. Once the user logs in using the new
password, they can change the password to what ever they desire.
Right.

I believe GUIDs are preferable because they can have meaning across
tables, data stores etc. When one account can have multiple
memberships, the different memberships can use the same membership ID.

If I implement my own provider (and I'm not sure I will), I may just use an
incrementing integer. I can't think of any problems with doing so.

BTW, do you happen to have any ideas about roles? How would something like
Roles.GetRolesForUser() work if I implement my own provider?

Thanks.
 
J

Jonathan Wood

sloan,
1. I think you need to write some code to keep tracked of
"LastActivityDate", and some rule that says anybody active in the last
(20?) minutes is "online".
Something like that.

Hadn't thought of that. Could just use a COUNT query WHERE the date is later
than 20 minutes ago.
2. That is up to you....one consideration is if you want the user to be
able to recover or reset their lost password.

That was the consideration I raised. But storing an unencrypted password is
less than ideal as well. I wasn't sure if there was a medium security
approach where perhaps I could decrypt the password. Otherwise, I'll just
have to reset it.
3. I now prefer guid's over ints for primary keys. It allows you (one
thing) to build relationships OUTSIDE of the database, because you're not
waiting on a return value for the SCOPE_IDENTITY. Not that this is a
compelling reason for a MembershipProvider.
There are pros and cons of guids/uniqueidentifiers. I guess as some
level, you just gotta pick one. And MS picked uniqueidentifiers.
Right.

http://imar.spaanjaars.com/QuickDocId.aspx?quickdoc=404
msdn.microsoft.com/asp.net/downloads/providers/

Coding up and seeing what others have done (including this Access version)
is a good exercise. I have yet to use Access as the Membership datastore,
but I learnt alot going through the tutorial and getting it to work.

Okay, thanks for the links.

BTW, do you happen to have any ideas about roles? How would something like
Roles.GetRolesForUser() work if I implement my own provider?

Thanks.
 
S

Scott Roberts

Jonathan Wood said:
If I implement my own provider (and I'm not sure I will), I may just use
an incrementing integer. I can't think of any problems with doing so.

Famous last words! ;) I'm not going to start another "Guid vs Integer"
debate, there are plenty of those out there already. But count me in the
"Guid" camp, especially for something like membership data. You say "just
use an incrementing integer" like that is easier. In fact, it's more
difficult (only slightly). ;)

I'm curious why you want to implement your own provider. I promise not to
try to talk you out of it, I just wonder why. We "piggy back" our custom
security stuff "on top of" the standard provider and I'm fairly happy with
it. It actually adds a little "security through obscurity" since someone
familiar with the standard membership stuff won't have a clue about what
else is going on on the server after they are "authenticated" by asp.net.
 
J

Jonathan Wood

Scott,
Famous last words! ;) I'm not going to start another "Guid vs Integer"
debate, there are plenty of those out there already. But count me in the
"Guid" camp, especially for something like membership data. You say "just
use an incrementing integer" like that is easier. In fact, it's more
difficult (only slightly). ;)

I trust you'll let me know when you're in the mood for articulating the
reasons why an integer might be a problem for what I'm doing.
I'm curious why you want to implement your own provider. I promise not to
try to talk you out of it, I just wonder why. We "piggy back" our custom
security stuff "on top of" the standard provider and I'm fairly happy with
it. It actually adds a little "security through obscurity" since someone
familiar with the standard membership stuff won't have a clue about what
else is going on on the server after they are "authenticated" by asp.net.

I'm still unsure of the best route, especially since I'm still pretty new to
the platform. I'm not interested in being told an approach is bad, but if
you'd care to articulate what problems there might be, or what might be
better, I am very interested.

Basically, I have three types of users (roles). I need to store some basic
information for each user, plus additional information that has different
fields depending on the user's role. So I'll need the primary table with
three additional tables, which store role-specific information.

If I create my own provider, I should be able to create relationships
between the specialized tables and the basic information table. This should
allow the database to enforce the relationship so that I could never delete
user information in the basic table without also deleting the associated
specialized information. (I think--I still need to solve some details
there.)

In addition, since creating a user involves adding records to more than one
table, I could use transactions. It doesn't appear that I can create a user
with the default provider and then create a record in the secondary table
all within a single transaction.

Finally, I started wondering if it might just be easier as I seem to spend a
lot of time trying to figure out exactly how the default provider works.
I've always enjoyed creating something more than trying to figure out what
someone else has done.

I don't believe profiles would work because profile data must be the same
for each user. In addition, much of what I've read warns against using
profiles because it is inefficient.

Thanks.
 
S

Scott Roberts

I trust you'll let me know when you're in the mood for articulating the
reasons why an integer might be a problem for what I'm doing.

This debate rages on, debated by people much smarter than me. This seems
like a "fair" link to start with (be sure to click the links inside the
page, as the page itself doesn't supply too much info). Google for more.

http://www.codinghorror.com/blog/archives/000817.html

For me, the determining factor is that Guids can be created by the
application, which let's me set up all keys (both primary and foreign)
inside the application then push it all to the database in a single
transaction. I'm sure there are techniques to accomplish this with
auto-incs, but with Guids I don't need "techniques" - it's trivial.
Basically, I have three types of users (roles). I need to store some basic
information for each user, plus additional information that has different
fields depending on the user's role. So I'll need the primary table with
three additional tables, which store role-specific information.

So create your "user" table and put a foreign key to the aspnet_Users table
on "UserId". Create your role-specific tables with foreign keys to your
"user" table.
If I create my own provider, I should be able to create relationships
between the specialized tables and the basic information table. This
should allow the database to enforce the relationship so that I could
never delete user information in the basic table without also deleting the
associated specialized information. (I think--I still need to solve some
details there.)

You can do this anyway. The aspnet membership tables are just SQL Server
tables. There's nothing magic about them. You can add relational constraints
to them all you want.
In addition, since creating a user involves adding records to more than
one table, I could use transactions. It doesn't appear that I can create a
user with the default provider and then create a record in the secondary
table all within a single transaction.

Good point ... maybe. See below.
Finally, I started wondering if it might just be easier as I seem to spend
a lot of time trying to figure out exactly how the default provider works.
I've always enjoyed creating something more than trying to figure out what
someone else has done.

I'm with you on this one. However, I'm not sure that creating your own
Membership provider is going to accomplish that. As you've already seen, the
membership interface defines the methods that you must implement and the
built-in login controls call those methods automatically. You still have to
work within the framework that someone else built. Which means that you
still need to understand that framework. And that framework may not allow
for passing all of your custom data around easily.

For example, the "Membership.CreateUser" method doesn't provide arguments
for user preferences. So you're going to need another method call to push
the preferences to the DB. That method call isn't going to be called by the
built-in controls, so you'll have to add events to those controls to call
the additional methods. I'm not sure that you are going to be able to get
all of that to happen in a single database transaction.

IMO, the membership interface is useful for supporting non-SQL Server
databases and little else. It's a DAL, not a place for customized business
rules. If you want purely custom business rules, I'd say abandon aspnet
membership and just roll your own. It's not like it's *that* hard. The whole
point of aspnet membership was to automate and simplify a routine web site
task. For us, it made more sense to use that tool "as is" then add more
information "on top" of it.

Scott
 
J

Jonathan Wood

Scott,
This debate rages on, debated by people much smarter than me. This seems
like a "fair" link to start with (be sure to click the links inside the
page, as the page itself doesn't supply too much info). Google for more.

http://www.codinghorror.com/blog/archives/000817.html

Thanks, I'll check it out.
You can do this anyway. The aspnet membership tables are just SQL Server
tables. There's nothing magic about them. You can add relational
constraints to them all you want.

Yes, I see that. For me, the issue is that now I not only need to learn all
about the ASP.NET membership interfaces, I also need a pretty thorough
understanding of the underlying tables, of which there are quite a few. It
reaches a point where whipping up something of my own almost seems easier.

Don't get me wrong though, I'd defintely prefer not to reinvent the wheel if
I don't have to. But my own implementation would be considerably simpler
than what's there and, of course, I'd understand it.
I'm with you on this one. However, I'm not sure that creating your own
Membership provider is going to accomplish that. As you've already seen,
the membership interface defines the methods that you must implement and
the built-in login controls call those methods automatically. You still
have to work within the framework that someone else built. Which means
that you still need to understand that framework. And that framework may
not allow for passing all of your custom data around easily.

So far, I'm pretty comfortable with the MembershipProvider interface. That
vast majority of the methods involved are self explanatory. And I could
simply get a reference to the provider, type cast it, and access any
additional functionality I implement.
For example, the "Membership.CreateUser" method doesn't provide arguments
for user preferences. So you're going to need another method call to push
the preferences to the DB. That method call isn't going to be called by
the built-in controls, so you'll have to add events to those controls to
call the additional methods. I'm not sure that you are going to be able to
get all of that to happen in a single database transaction.

I don't know what you mean by user preferences. If you are talking about
profiles, I don't see any reason to use those if I implement my own
provider. If you mean something else, perhaps you could clarify.

(BTW, for my current application, users will not create their own accounts.
They will be created by other users, such as administrators.)
IMO, the membership interface is useful for supporting non-SQL Server
databases and little else. It's a DAL, not a place for customized business
rules. If you want purely custom business rules, I'd say abandon aspnet
membership and just roll your own. It's not like it's *that* hard. The
whole point of aspnet membership was to automate and simplify a routine
web site task. For us, it made more sense to use that tool "as is" then
add more information "on top" of it.

I just want the easiest route and can't really determine what that is right
now. Completely redoing the entire part seems like a bit more work to
me--something I'd love to take on but not now.

Thanks.
 

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