I am building a custom MembershipProvider more precisely the GetUser function.
Therefor i have a custom MembershipUser.
public class CustomMemberShipUser : MembershipUser
{
public CustomMemberShipUser (
string providerName,
string email,
object providerUserKey,
string name,
string passwordQuestion,
string comment,
bool isApproved,
bool isLockedOut,
DateTime creationDate,
DateTime lastLoginDate,
DateTime lastActivityDate,
DateTime lastPasswordChangedDate,
DateTime lastLockoutDate
): base(
providerName, email, providerUserKey, name, passwordQuestion,
comment, isApproved, isLockedOut, creationDate, lastLoginDate,
lastActivityDate, lastPasswordChangedDate, lastLockoutDate)
{
}
}
In the GetUser function of the MembershipProvider i get the user data and put them into the CustomMemberShipUser.
public override MembershipUser GetUser(string email, bool userIsOnline)
{
User u = _db.Users.Where(x => x.Email == email).First();
CustomMemberShipUser customUser = new CustomMemberShipUser (
"CustomMemberShipUser ",
u.Email,
u.id,
u.Email,
"",
"",
true,
false,
u.CreateDate,
DateTime.MinValue,
DateTime.MinValue,
DateTime.MinValue,
DateTime.MinValue);
return customUser ;
}
As you can see i use the email as name for the MemberShip and i don't need most of the other parameters.
Is there a way to make the call simpler? I don't want to initalize the MembershipUser with empty Strings and minimal Date values.
Thanks in advance
Could you adapt your CustomMembershipUser to do the 'padding' for you
public class CustomMemberShipUser : MembershipUser
{
public CustomMemberShipUser (
string email,
object providerUserKey,
): base(
"CustomMemberShipUser", email, providerUserKey, email, String.Empty,
String.Empty, true, false, DateTime.MinValue, DateTime.MinValue,
DateTime.MinValue, DateTime.MinValue, DateTime.MinValue)
{
}
}
It doesn't solve the problem but it will tidy up your provider which will become
public override MembershipUser GetUser(string email, bool userIsOnline)
{
User u = _db.Users.Where(x => x.Email == email).First();
CustomMemberShipUser customUser = new CustomMemberShipUser (u.Email, u.id);
return customUser ;
}
I presume your CustomMembershipUser is exposing some additional properties that you are not showing us. As it stands you could just return a MembershipUser. With the above the only benefit your CustomMembershipUser gives you is the cleaner construction in your CustomMembershipProvider
Related
I have set up an external login (Google) in my ASP.NET Core application. I am finding it hard to get the User Name / Email after login. I can see the email stored in AspNetUsers table But I don't see User Name anywhere.
I searched over and found this code:
var userId = this.User.FindFirstValue(ClaimTypes.NameIdentifier);
But this is giving me userId as is present in table AspNetUsers. ClaimTypes.Email returns null but the value is present in table (probably this email is something else). I want to fetch User Name and User Email. Is it possible?
Do you have access to SignInManager or can you inject it? If yes, then this is how you would access user id (username), email, first & last name:
public class MyController : Microsoft.AspNetCore.Mvc.Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
public MyController (
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager
)
{
_userManager = userManager;
_signInManager = signInManager;
}
public async Task<IActionResult> MyAction(){
ExternalLoginInfo info = await _signInManager.GetExternalLoginInfoAsync();
string userId = info.Principal.GetUserId()
string email = info.Principal.FindFirstValue(ClaimTypes.Email);
string FirstName = info.Principal.FindFirstValue(ClaimTypes.GivenName) ?? info.Principal.FindFirstValue(ClaimTypes.Name);
string LastName = info.Principal.FindFirstValue(ClaimTypes.Surname);
}
}
GetUserId extension:
public static class ClaimsPrincipalExtensions
{
public static string GetUserId(this ClaimsPrincipal principal)
{
if (principal == null)
return null; //throw new ArgumentNullException(nameof(principal));
string ret = "";
try
{
ret = principal.FindFirst(ClaimTypes.NameIdentifier)?.Value;
}
catch (System.Exception)
{
}
return ret;
}
}
Whats the best way for authenticating web users and storing their details which i have a class for:
Should i use sessions or the forms auth cookie?
How can i access it from either way say like userclass.username?
I would like to store quite a lot of user information to stop database calls for stuff like: user type, user a full name, addresss, post code, foo1, foo2, foo3, foo4 and more. I know this could go in session or auth cookie user data. This question relates and links to https://stackoverflow.com/questions/18393122/whats-the-best-way-to-authenticate-a-user-and-store-user-details-sessions-or-fo which i have not had any help on
Really could do with some help and advice here as i have a few system which i need to do this for. Any comments appreciated.
thanks
************************************ Links *****************************
My code based roughly on:
http://www.shawnmclean.com/blog/2012/01/storing-strongly-typed-object-user-profile-data-in-asp-net-forms-authentication-cookie/
http://www.danharman.net/2011/07/07/storing-custom-data-in-forms-authentication-tickets/
************************************ EDIT *****************************
custom identity module
Public Module IdentityExtensions
Sub New()
End Sub
Private _CustomIdentityUser As CustomIdentityUser
<System.Runtime.CompilerServices.Extension> _
Public Function CustomIdentity(identity As System.Security.Principal.IIdentity) As CustomIdentityUser
'If _CustomIdentityUser Is Nothing Then
'_CustomIdentityUser = DirectCast(identity, CustomIdentityUser)
_CustomIdentityUser = Nothing
If identity.GetType = GetType(FormsIdentity) Then
_CustomIdentityUser = New CustomIdentityUser(DirectCast(identity, FormsIdentity).Ticket)
Else
If identity.IsAuthenticated Then
FormsAuthentication.RedirectToLoginPage()
End If
End If
Return _CustomIdentityUser
End Function
End Module
My Custom identity user
Public Class CustomIdentityUser
Implements System.Security.Principal.IIdentity
Private ticket As System.Web.Security.FormsAuthenticationTicket
Private _Auth As Auth
Public Sub New(ticket As System.Web.Security.FormsAuthenticationTicket)
Me.ticket = ticket
_Auth = New projectabc.Auth(Me.ticket)
End Sub
Public ReadOnly Property Auth As Auth
Get
Return Me._Auth
End Get
End Property
Public ReadOnly Property Username As String
Get
Return Auth.Username
End Get
End Property
Public ReadOnly Property UserType As Enumerations.EnumUserType
Get
Return Auth.UserType
End Get
End Property
Public ReadOnly Property OwnerType As Enumerations.EnumOwnerType
Get
Return Auth.OwnerType
End Get
End Property
Public ReadOnly Property AuthenticationType As String Implements System.Security.Principal.IIdentity.AuthenticationType
Get
Return "Custom"
End Get
End Property
Public ReadOnly Property IsAuthenticated As Boolean Implements System.Security.Principal.IIdentity.IsAuthenticated
Get
Return ticket IsNot Nothing
End Get
End Property
Public ReadOnly Property Name As String Implements System.Security.Principal.IIdentity.Name
Get
Return Username
End Get
End Property
End Class
Then as you can see the user class calls an auth class which basically has all the properties for the user and gets and set it etc.
Public Class Auth
Inherits BaseUser
Public Property _ticket As Web.Security.FormsAuthenticationTicket
Public RememberMe As Boolean
Private _IssueDate As DateTime?
Public ReadOnly Property IssueDate As DateTime?
Get
Return _IssueDate
End Get
End Property
Private _Expired As Boolean
Public ReadOnly Property Expired As Boolean
Get
Return _Expired
End Get
End Property
Private _Expiration As DateTime?
Public ReadOnly Property Expiration As DateTime?
Get
Return _Expiration
End Get
End Property
Public Sub New(ticket As System.Web.Security.FormsAuthenticationTicket)
Me._ticket = ticket
Dim SignOutUser As Boolean = False
Try
If Not GetUserDetails() Then
SignOutUser = True
End If
Catch ex As Exception
SignOutUser = True
End Try
If SignOutUser Then
HttpContext.Current.Response.Redirect("~/", True)
SignOut()
End If
End Sub
Public ReadOnly Property IsAuthenticated() As Boolean
Get
Return HttpContext.Current.User.Identity.IsAuthenticated
End Get
End Property
Public Function SetAuthCookie() As Int16
Dim encTicket As String
Dim userData As String = CreateUserDataString()
If userData.Length > 0 And userData.Length < 4000 Then
Dim cookiex As HttpCookie = FormsAuthentication.GetAuthCookie(MyBase.Username, True)
Dim ticketx As FormsAuthenticationTicket = FormsAuthentication.Decrypt(cookiex.Value)
'Dim newTicket = New FormsAuthenticationTicket(ticket.Version, ticket.Name, ticket.IssueDate, ticket.Expiration, ticket.IsPersistent, userData, ticket.CookiePath)
'encTicket = FormsAuthentication.Encrypt(newTicket)
'Use existing cookie. Could create new one but would have to copy settings over...
'cookie.Value = encTicket
'cookie.Expires = newTicket.Expiration.AddHours(24)
'HttpContext.Current.Response.Cookies.Add(cookie)
Dim ticket As New FormsAuthenticationTicket(1, ticketx.Name, DateTime.Now, ticketx.Expiration, False, userData, ticketx.CookiePath)
encTicket = FormsAuthentication.Encrypt(ticket)
cookiex.Value = encTicket
'Dim cookie As New HttpCookie(FormsAuthentication.FormsCookieName, encTicket)
HttpContext.Current.Response.Cookies.Add(cookiex)
Else
Throw New ArgumentOutOfRangeException("User data length exceeds maximum", New ArgumentOutOfRangeException)
End If
Return encTicket.Length
End Function
Public Function GetUserDetails() As Boolean
Dim valid As Boolean = False
If _ticket IsNot Nothing Then
With _ticket
RememberMe = .IsPersistent
Username = .Name
_IssueDate = .IssueDate
_Expired = .Expired
_Expiration = .Expiration
Try
If .UserData.Length > 0 Then
valid = SetUserDataFromString(.UserData)
Else
'we have a problem
Return False
End If
Catch ex As Exception
'sign them out as they may have a cookie but the code may have changed so it errors thus make them login again.
'SignOut()
Throw ex
End Try
End With
End If
Return valid
End Function
Private Function CreateUserDataString() As String
Dim sData As New System.Text.StringBuilder
With sData
.Append(MyBase.UserID)
.Append("|") 'delimeter we are using
.Append(Int16.Parse(MyBase.UserType))
.Append("|")
.Append(Int16.Parse(MyBase.Security))
.Append("|") 'delimeter we are using
.Append(MyBase.FirstName)
.Append("|")
.Append(MyBase.LastName)
.Append("|")
.Append(MyBase.foo1)
.Append("|")
.Append(MyBase.foo2)
.Append("|")
.Append(MyBase.foo3)
.Append("|")
.Append(MyBase.foo4)
End With
Return sData.ToString
End Function
Public Function SetUserDataFromString(userData As String) As Boolean
Dim valid As Boolean = False
Dim sData As New System.Text.StringBuilder
'check we have a delimeter
Dim arUserData As String() = userData.Split("|")
Try
If arUserData.Count >= 9 Then '9 because that the user only stuff
With arUserData
MyBase.UserID = arUserData(0)
MyBase.UserType = arUserData(1)
MyBase.Security = arUserData(2)
MyBase.FirstName = arUserData(3)
MyBase.LastName = arUserData(4)
MyBase.foo1 = arUserData(5)
MyBase.foo2 = arUserData(6)
MyBase.foo3 = arUserData(7)
MyBase.foo4 = arUserData(8)
End With
valid = True
Else
valid = False
End If
Catch ex As Exception
Throw New ArgumentOutOfRangeException("User data length to short", New ArgumentOutOfRangeException)
End Try
Return valid
End Function
Public Sub SignOut()
FormsAuthentication.SignOut()
End Sub
As you have posted the code may be you would get some good answer. I would try to answer as per my understanding, hope that helps.
From where do you invoke CustomIdentity? In the first method in the Else part I think you might want to use Not IsAuthenticated if you want the user to redirect to the login page. Most of the times if the ticket is invalid you don't even have to do it the framework does it for you.
In the CustomIdentityUser you have a private member _Auth which you never use while returning some values through the properties. You are using Auth.UserName instead of _Auth.UserName, I am not sure how that works unless the UserName is static member.
Why is you custom Identity dependent on Auth? You can pass on the required data to the custom identity through contructor or by exposing public setters. Why do you need the auth ticket inside the identity?
The Auth Class has the expiration date and other stuff which is not required. You can have a simple User class to store the basic details of the user. Why are you redirecting the user from the constructor of Auth?
The GetUserDetails and SetUserDataFromString I do not know why do you need all these methods. Its just matter of Serializing and Deserializing the User class.
I understand that you must have referred some blog out there to implement this authentication, but you have lot of scope to simplify this.
Read this post. Specifically how the custom principal is implemented and how the authticket is set and the PostAuthenticateRequest method.
Here is some sample code that might help
interface ICustomPrincipal : IPrincipal
{
int UserId { get; set; }
string FirstName { get; set; }
string LastName { get; set; }
}
public class CustomPrincipal : ICustomPrincipal
{
public CustomPrincipal()
{
}
public CustomPrincipal(string userName)
{
Identity = new GenericIdentity(userName);
}
public int UserId
{
get;
set;
}
public string FirstName
{
get;
set;
}
public string LastName
{
get;
set;
}
public IIdentity Identity
{
get;
private set;
}
public bool IsInRole(string role)
{
return false;
}
}
public class User
{
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
}
public static class FormsAuthHelper
{
public static void SetAuthTicket(User user, HttpContextBase context)
{
var serializer = new JavaScriptSerializer();
var userData = serializer.Serialize(user);
var authTicket = new FormsAuthenticationTicket(
1, user.UserName,
DateTime.Now, DateTime.Now.AddMinutes(30),
false, userData);
var ticket = FormsAuthentication.Encrypt(authTicket);
var faCookie = new HttpCookie(FormsAuthentication.FormsCookieName, ticket);
context.Response.Cookies.Add(faCookie);
}
public static void Logout()
{
FormsAuthentication.SignOut();
FormsAuthentication.RedirectToLoginPage();
}
public static CustomPrincipal GetPrincipal(User user)
{
return new CustomPrincipal(user.UserName) { FirstName = user.FirstName, LastName = user.LastName, UserId = user.EntityId };
}
}
Post authenticate request event looks like this
protected void Application_PostAuthenticateRequest(object sender, EventArgs e)
{
var authCookie = Context.Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie == null || authCookie.Value == string.Empty)
return;
try
{
var ticket = FormsAuthentication.Decrypt(authCookie.Value);
var serializer = new JavaScriptSerializer();
var user = serializer.Deserialize<User>(ticket.UserData);
var newUser = FormsAuthHelper.GetPrincipal(user);
HttpContext.Current.User = newUser;
}
catch
{
//do nothing
}
}
And finally when the user logs in
public ActionResult Login(LoginModel loginModel)
{
if (ModelState.IsValid)
{
var user = _userRepository.Get(x => x.UserName == loginModel.UserName).SingleOrDefault();
if (user != null && PasswordHash.ValidatePassword(loginModel.Password, user.Password))
{
FormsAuthHelper.SetAuthTicket(user, HttpContext);
return RedirectToAction("Index", "Home");
}
ModelState.AddModelError("NotFound", "User not found");
}
return View(loginModel);
}
Until now I have done all authentication work in my MVC3 app, i.e. validate a member, and create a member, through my MemberRepository class. I would now like to go official, with a custom MembershipProvider. So far I have only gleaned that I really need to override this class's ValidateUser method, and since I am not using a Login control, I'm not even sure I absolutely have to do this.
Overriding methods like GetUser and CreateUser brings uninvited types to my party, like MembershipUser, where I have a finely crafted Member class. Please can someone clear up for me whether or not I really need a custom membership provider, if I'm not going to use any built-in controls or the admin tool, and if I do, should I confine my overrides to the absolutely necessary, which is what?
Here's one I wrote for unit testing. It's about as minimal as can be.
public class MockMembershipProvider : MembershipProvider
{
public IList<MembershipUser> Users { get; private set; }
private string _applicationName;
public override string ApplicationName
{
get
{
return _applicationName;
}
set
{
_applicationName = value;
}
}
public override bool ChangePassword(string username, string oldPassword, string newPassword)
{
throw new NotImplementedException();
}
public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer)
{
throw new NotImplementedException();
}
public override MembershipUser CreateUser(
string username,
string password,
string email,
string passwordQuestion,
string passwordAnswer,
bool isApproved,
object providerUserKey,
out MembershipCreateStatus status)
{
var user = new MembershipUser(ProviderName, username, username, email, passwordQuestion, null, isApproved, false, DateTime.Now, DateTime.Now, DateTime.Now, DateTime.Now, DateTime.Now);
Users.Add(user);
status = MembershipCreateStatus.Success;
return user;
}
public override bool DeleteUser(string username, bool deleteAllRelatedData)
{
var u = Users.Where(mu => mu.UserName.Equals(username, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
if (u == null) return false;
Users.Remove(u);
return true;
}
public override bool EnablePasswordReset
{
get { return false; }
}
public override bool EnablePasswordRetrieval
{
get { return false; }
}
public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
{
throw new NotImplementedException();
}
public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
{
var users = (from u in Users
where u.UserName.Equals(usernameToMatch, StringComparison.OrdinalIgnoreCase)
select u).ToList();
totalRecords = users.Count;
return ToMembershipUserCollection(users);
}
public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
{
var list = Users.Skip(pageIndex * pageSize).Take(pageSize);
totalRecords = list.Count();
var result = new MembershipUserCollection();
foreach (var u in list)
{
result.Add(u);
}
return result;
}
public override int GetNumberOfUsersOnline()
{
return Users.Count();
}
public override string GetPassword(string username, string answer)
{
throw new NotImplementedException();
}
public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
{
return (from u in Users
where u.ProviderUserKey.ToString() == providerUserKey.ToString()
select u).FirstOrDefault();
}
public override MembershipUser GetUser(string username, bool userIsOnline)
{
return (from u in Users
where u.UserName.Equals(username, StringComparison.OrdinalIgnoreCase)
select u).FirstOrDefault();
}
public override string GetUserNameByEmail(string email)
{
return (from u in Users
where u.Email.Equals(email, StringComparison.OrdinalIgnoreCase)
select u.UserName).FirstOrDefault();
}
public override int MaxInvalidPasswordAttempts
{
get { return 3; }
}
public override int MinRequiredNonAlphanumericCharacters
{
get { return 1; }
}
public override int MinRequiredPasswordLength
{
get { return 6; }
}
public override int PasswordAttemptWindow
{
get { return 10; }
}
public override MembershipPasswordFormat PasswordFormat
{
get { throw new NotImplementedException(); }
}
public override string PasswordStrengthRegularExpression
{
get { return null; }
}
public override string Name
{
get
{
return ProviderName;
}
}
public string ProviderName { get; set; }
public override string ResetPassword(string username, string answer)
{
throw new NotImplementedException();
}
public override bool RequiresQuestionAndAnswer
{
get { return false; }
}
public override bool RequiresUniqueEmail
{
get { return true; }
}
private MembershipUserCollection ToMembershipUserCollection(IEnumerable<MembershipUser> users)
{
var result = new MembershipUserCollection();
foreach (var u in users)
{
result.Add(u);
}
return result;
}
public override bool UnlockUser(string userName)
{
return true;
}
public override void UpdateUser(MembershipUser user)
{
var oldUser = Users.Where(u => u.UserName.Equals(user.UserName, StringComparison.OrdinalIgnoreCase)).Single();
var index = Users.IndexOf(oldUser);
Users[index] = user;
}
public override bool ValidateUser(string username, string password)
{
throw new NotImplementedException();
}
public MockMembershipProvider()
{
this.ProviderName = "MockMembershipProvider";
Users = new List<MembershipUser>();
}
}
public class FakeMembershipProvider : MockMembershipProvider
{
public FakeMembershipProvider(string name)
{
this.ProviderName = name ?? "MockMembershipProvider";
}
public override MembershipUser CreateUser(
string username,
string password,
string email,
string passwordQuestion,
string passwordAnswer,
bool isApproved,
object providerUserKey,
out MembershipCreateStatus status)
{
status = MembershipCreateStatus.ProviderError;
var user = new MockMembershipUser();
user.Password = password;
user.User = username;
user.UserKey = providerUserKey;
Users.Add(user);
status = MembershipCreateStatus.Success;
return user;
}
}
public class MockMembershipUser : MembershipUser
{
public string Password { get; set; }
public string User { get; set; }
public object UserKey { get; set; }
public override string UserName { get { return User; } }
public override string Comment { get; set; }
public override object ProviderUserKey { get { return UserKey; } }
public override string GetPassword()
{
return Password ?? string.Empty;
}
Custom MembershipProvider
It is possible to get some nice security features "for free" if you're using a MembershipProvider: You can set up web.config to redirect every non-authenticated user to a login page, for instance. Or you can set up specific parts of the site to only be visible to users with specific roles. If these features don't make sense for your project, or if you're already implementing their equivalent in another way, there's not much point implementing a custom MembershipProvider.
SqlMembershipProvider
One other possibility you may want to consider is actually switching your own implementation to use the SqlMembershipProvider to handle membership functions.
The SqlMembershipProvider provides a robust, proven platform for the common tasks that are annoying to have to reinvent for every project: account creation, validation, deletion, locking, password resets, basic roles, etc. If you've already done all of this yourself without using the SqlMembershipProvider, there really isn't any point creating one just for the sake of having it. However, you should be careful, because there's a good chance that you've done something wrong in your own implementation. For example,
Are you storing passwords as plain text, or as hashes?
Are you open to Rainbow Table attacks, or are you salting your hashes?
Do you lock people's accounts after you've seen 50 or so invalid password attempts in a row, or do you let hackers just keep pounding away until they've brute-forced their way into someone's account?
The SqlMembershipProvider has already addressed all these issues in an easily configurable manner. You may want to have your own membership interfaces and DTOs simply wrap this default MembershipProvider just so you don't have to worry about these various concerns. That way, most of your code doesn't have to interact with these "uninvited types," but you still get the advantages of a widely-used and proven security framework on the back end.
Do you want to decouple your web application from MembershipRepository?
If so, implement all of the same functionality in a custom MembershipProvider so that your app will only depend on the .NET Membership classes (aside from your web.config).
If not, then don't bother.
How to change user password for logged in user (and any field in user profile) if I use Silverlight Business Application?
There is no built in mechanism to change password in Silverlight.
You need to implement your own service for that.
For example:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class SecurityService : ISecurityService
{
public bool ChangePassword(string oldPassword, string newPassword)
{
if(!HttpContext.Current.User.Identity.IsAuthenticated)
return false;
return Membership.Provider.ChangePassword(HttpContext.Current.User.Identity.Name, oldPassword, newPassword);
}
...
}
If this answers your question, please "mark it as answer".
So, I created Domain Service with only one method:
[EnableClientAccess()]
public class DomainChangePassword : DomainService
{
[ServiceOperation]
public bool UserChangePassword(string userName, string oldPassword, string newPassword)
{
if (Membership.ValidateUser(userName, oldPassword))
{
MembershipUser memUser = Membership.GetUser(userName);
return memUser.ChangePassword(oldPassword, newPassword);
}
return false;
}
}
try:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class SecurityService : ISecurityService
{
public bool ChangePassword(string oldPassword, string newPassword)
{
if(!HttpContext.Current.User.Identity.IsAuthenticated)
return false;
return Membership.Provider.ChangePassword(HttpContext.Current.User.Identity.Name, oldPassword, newPassword);
}
...
}
I am going round in circles and need some help in implementing a Custom MembershipUser so that I can add my own custom Properties to the MembershipUser.
I have been following the example on this site: How to: Implement a Custom Membership User
The problem I am having is in the constructor of CustomMembershipUser, I think.
My CustomMembershipUser has these three additional Properties: firstName, middleName, lastName.
public class CustomMembershipProvider : MembershipProvider
{
public override MembershipUser GetUser(string username, bool userIsOnline)
{
//.... Get data from database
MembershipUser baseUser = new MembershipUser(this.Name,
username,
userId,
email,
"",
comment,
isApproved,
isLockedOut,
dtCreate,
dtLastLogin,
dtLastActivity,
DateTime.Now,
dtLastLockoutDate);
return new CustomMembershipUser(baseUser, firstName, middleName, lastName)
}
}
public class CustomMembershipUser : MembershipUser
{
private string _firstName;
public string FirstName { get { return _firstName; } set { _firstName = value; } }
private string _middleName;
public string MiddleName { get { return _middleName; } set { _middleName = value; } }
private string _lastName;
public string LastName { get { return _lastName; } set { _lastName = value; } }
public CustomMembershipUser(MembershipUser baseuser, string firstname, string middlename, string lastname)
{
_firstName = firstname;
_middleName = middlename;
_lastName = lastname;
new CustomMembershipUser(baseuser); // DO I NEED THIS?? HOW TO IMPLEMENT??
}
}
I am calling it like so:
MembershipUser mu = Membership.GetUser(UserName);
CustomMembershipProvider p = (CustomMembershipProvider)Membership.Provider;
MembershipUser memUser = p.GetUser(UserName, true);
object userId = memUser.ProviderUserKey;
The ProviderUserKey is null and so are the other values.
How can I obtain the addition Properties I added?
Thanks
This is working for me:
public class CustomMembershipUser : MembershipUser
{
public CustomMembershipUser(
string providerName,
string name,
object providerUserKey,
string email,
string passwordQuestion,
string comment,
bool isApproved,
bool isLockedOut,
DateTime creationDate,
DateTime lastLoginDate,
DateTime lastActivityDate,
DateTime lastPasswordChangedDate,
DateTime lastLockoutDate
)
: base(providerName, name, providerUserKey, email, passwordQuestion,
comment, isApproved, isLockedOut, creationDate, lastLoginDate,
lastActivityDate, lastPasswordChangedDate, lastLockoutDate)
{
}
// Add additional properties
public string CustomerNumber { get; set; }
}
public class CustomMembershipProvider : MembershipProvider
{
public override MembershipUser GetUser(string username, bool userIsOnline)
{
if (string.IsNullOrEmpty(username))
{
// No user signed in
return null;
}
// ...get data from db
CustomMembershipUser user = new CustomMembershipUser(
"CustomMembershipProvider",
db.Username,
db.UserId,
db.Email,
"",
"",
true,
false,
db.CreatedAt,
DateTime.MinValue,
DateTime.MinValue,
DateTime.MinValue,
DateTime.MinValue);
// Fill additional properties
user.CustomerNumber = db.CustomerNumber;
return user;
}
}
// Get custom user (if allready logged in)
CustomMembershipUser user = Membership.GetUser(true) as CustomMembershipUser;
// Access custom property
user.CustomerNumber
Based on my own experience trying to do much of the same, trying to use the MembershipProvider to do this will be an ultimately frustrating and counterintuitive experience.
The idea of the membership provider model isn't to change or augment what the definition of a user is, as you're trying to do - it is to allow the Framework an alternate means of accessing the information that has already been defined as belonging to a "MembershipUser".
I think what you're really looking for is a user profile. Using ASP.NET profiles is boatloads easier than implementing your own provider. You can find the overview here.
Just so you know, I've tried to go down the MembershipProvider path before, and it's a long and windy one. You might see if just creating classes that implement IPrincipal and IIdentity will satisfy your needs, since they entail a lot less overhead.