How much of MembershipProvider do I *have* to override in MVC3 - asp.net

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.

Related

RoleManager in asp.net core web app working with different tables

I have Four tables (Student, AspNetUsers, AspNetUserRoles and AspNetRoles).
I have three roles (user, Teacher, Admin).
The teacher/Admin creates a user account(fields ie Email, phone, address, role) with the user role and this is saved in the student table. The teacher gives an Email to the student.
The student creates an account with an Email(Created by teacher/Admin) and password and this is saved in the AspNetUsers table.
My Question Is: How to assign a role to the student that is given by the Teacher/Admin in AspNetUserRoles table (UserID and UserRoleId). UserId is in AspNetUsers table and UserRoleId is in student table
public class RolesAdminController : Controller
{
private ApplicationUserManager _userManager;
public ApplicationUserManager UserManager;
private ApplicationUserManager _userManager;
public RolesAdminController(ApplicationUserManager userManager,
ApplicationRoleManager roleManager, ApplicationSignInManager signInManager)
{
UserManager = userManager;
RoleManager = roleManager;
SignInManager = signInManager;
}
[HttpPost]
public async Task<IActionResult> Register(string returnUrl = null)
{
returnUrl = returnUrl ?? Url.Content("~/");
if(ModelState.IsValid){
var user = new IdentityUser{Username = Input.Name, Email = Input.Email};
var result = await _UserManager.CreateAsync(user,Input.Password);
return RedirectToAction("Index");
}
}
I have no idea.. What should I add in my code to do it? Assign a role to the student that should be done while the student Register the account.
Need Help
Here is hoping that you have your startup code wired up correctly to include the AddRoleManager method like so
services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
Then your code is tweeked a bit since it seems as though you are tring to make the call from an api endpoint instead of an actual web page, hence issues such as redirecting to a return url will be handled by the caller when they assess whether the response from the api call they made succeeded or not. Note also that I use IdentityUser instead of ApplicationUser, but you can swap it out if you are using the later. Anyway, the code should give you a gist of how to implement it in your own project
public class RolesAdminController : ControllerBase
{
private UserManager<IdentityUser> userManager;
private readonly RoleManager<IdentityRole> roleManager;
public RolesAdminController(UserManager<IdentityUser> userManager,
RoleManager<IdentityRole> roleManager)
{
this.userManager = userManager;
this.roleManager = roleManager;
}
[HttpPost]
public async Task<IActionResult> Register(InputDto inputDto)
{
inputDto ??= new InputDto(); // if input is null, create new input
List<string> errors = inputDto.Errors();
if (errors.Any())
{
return BadRequest(errors);
}
var user = new IdentityUser(){ UserName = inputDto.Name, Email = inputDto.Email };
var result = await userManager.CreateAsync(user, inputDto.Password);
if (!result.Succeeded)
{
return BadRequest(result.Errors); //You can turn that errors object into a list of string if you like
}
string roleName = "";
switch (inputDto.RoleType)
{
case RoleType.User:
roleName = "user";
break;
case RoleType.Teacher:
roleName = "Teacher";
break;
case RoleType.Admin:
roleName = "Admin";
break;
default:
break;
}
//Checking if role is in not system
if(!(await roleManager.RoleExistsAsync(roleName)))
{
await roleManager.CreateAsync(new IdentityRole(roleName));
}
await userManager.AddToRoleAsync(user, roleName);
return Ok(); //Or you can return Created(uriToTheCreatedResource);
}
}
public class InputDto
{
public string Name { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public RoleType RoleType { get; set; }
public List<string> Errors()
{
List<string> vtr = new List<string>();
if (String.IsNullOrWhiteSpace(Name))
{
vtr.Add("Name Cannot be null");
}
if (String.IsNullOrWhiteSpace(Email))
{
vtr.Add("Email Cannot be null");
}
//Do the rest of your validation
return vtr;
}
}
public enum RoleType
{
User, Teacher, Admin
}

How to return a Mono<ResponseEntity> where the response entity can be of two different types

I am new to Spring Webflux / Reactor Core and am trying to perform the following functionality:
call userservice.LoginWebApp()
If a user is returned, return ResponseEntity of type "User". If empty, Return ResponseEntity of type "String"
The following code gives a type error as .defaultIfEmpty() expects ResponseEntity of type user. Can you please advise on the correct operator / method to implement this functionality.
#PostMapping("api/user/login/webApp")
public Mono<ResponseEntity> login(#RequestBody Credentials credentials, ServerWebExchange serverWebExchange) {
return userService.loginWebApp(credentials, serverWebExchange)
.map(user -> ResponseEntity.status(HttpStatus.OK).body(user))
.defaultIfEmpty(ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid username or password"));
}
You can use the cast operator to downcast out of the generic, and I believe WebFlux will still be able to marshal the User and the String:
#PostMapping("api/user/login/webApp")
public Mono<ResponseEntity> login(#RequestBody Credentials credentials, ServerWebExchange serverWebExchange) {
return userService.loginWebApp(credentials, serverWebExchange)
.map(user -> ResponseEntity.status(HttpStatus.OK).body(user))
.cast(ResponseEntity.class)
.defaultIfEmpty(ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid username or password"));
}
I would do the following:
Make a base class for responses
abstract class Response {
}
Make separate classes for every kind of response (like UserResponse, ErrorResponse, NotFoundResponse etc) and extend them from the base Response class
class UserResponse extends Response {
private String login;
private String password;
public UserResponse(String login, String password) {
this.login = login;
this.password = password;
}
#JsonGetter("login")
public String getLogin() {
return login;
}
#JsonSetter("login")
public void setLogin(String login) {
this.login = login;
}
#JsonGetter("password")
public String getPassword() {
return password;
}
#JsonSetter("password")
public void setPassword(String password) {
this.password = password;
}
}
class ErrorResponse extends Response {
private String errorMessage;
public ErrorResponse(String errorMessage) {
this.errorMessage = errorMessage;
}
#JsonGetter("error_message")
public String getErrorMessage() {
return errorMessage;
}
#JsonSetter("error_message")
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
}
Explicitly set the type of return value Mono<ResponseEntity<Response>>
And that's it.
#GetMapping("/test/{login}")
public Mono<ResponseEntity<Response>> test(#PathVariable(value = "login") String login) {
return loginWebApp(login)
.map(userResponse -> ResponseEntity.status(HttpStatus.OK).body(userResponse))
.defaultIfEmpty(ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(new ErrorResponse("bad login")));
}
Now let's try it with bad login:
And good login:
Full code can be found here

Disable User in ASPNET identity 2.0

I am looking for a way to disable the user instead of deleting them from the system, this is to keep the data integrity of the related data. But seems ASPNET identity only offers Delete Acccount.
There is a new Lockout feature, but it seems to lockout can be controlled to disable user, but only lock the user out after certain number of incorrect password tries.
Any other options?
When you create a site with the Identity bits installed, your site will have a file called "IdentityModels.cs". In this file is a class called ApplicationUser which inherits from IdentityUser.
// You can add profile data for the user by adding more properties to your ApplicationUser class, please visit https://devblogs.microsoft.com/aspnet/customizing-profile-information-in-asp-net-identity-in-vs-2013-templates/ to learn more.
public class ApplicationUser : IdentityUser
There is a nice link in the comments there, for ease click here
This tutorial tells you exactly what you need to do to add custom properties for your user.
And actually, don't even bother looking at the tutorial.
add a property to the ApplicationUser class, eg:
public bool? IsEnabled { get; set; }
add a column with the same name on the AspNetUsers table in your DB.
boom, that's it!
Now in your AccountController, you have a Register action as follows:
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email, IsEnabled = true };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
I've added the IsEnabled = true on the creation of the ApplicationUser object. The value will now be persisted in your new column in the AspNetUsers table.
You would then need to deal with checking for this value as part of the sign in process, by overriding PasswordSignInAsync in ApplicationSignInManager.
I did it as follows:
public override Task<SignInStatus> PasswordSignInAsync(string userName, string password, bool rememberMe, bool shouldLockout)
{
var user = UserManager.FindByEmailAsync(userName).Result;
if ((user.IsEnabled.HasValue && !user.IsEnabled.Value) || !user.IsEnabled.HasValue)
{
return Task.FromResult<SignInStatus>(SignInStatus.LockedOut);
}
return base.PasswordSignInAsync(userName, password, rememberMe, shouldLockout);
}
Your mileage may vary, and you may not want to return that SignInStatus, but you get the idea.
The default LockoutEnabled property for a User is not the property indicating if a user is currently being locked out or not. It's a property indicating if the user should be subject to lockout or not once the AccessFailedCount reaches the MaxFailedAccessAttemptsBeforeLockout value. Even if the user is locked out, its only a temporary measure to bar the user for the duration of LockedoutEnddateUtc property. So, to permanently disable or suspend a user account, you might want to introduce your own flag property.
You don't need to create a custom property. The trick is to set the
LockoutEnabled property on the Identity user AND set the LockoutoutEndDateUtc to a future date from your code to lockout a user. Then, calling the UserManager.IsLockedOutAsync(user.Id) will return false.
Both the LockoutEnabled and LockoutoutEndDateUtc must meet the criteria of true and future date to lockout a user. If, for example, the LockoutoutEndDateUtc value is 2014-01-01 00:00:00.000 and LockoutEnabled is true, calling theUserManager.IsLockedOutAsync(user.Id) will still return true. I can see why Microsoft designed it this way so you can set a time span on how long a user is locked out.
However, I would argue that it should be if LockoutEnabled is true then user should be locked out if LockoutoutEndDateUtc is NULL OR a future date. That way you don't have to worry in your code about setting two properties (LockoutoutEndDateUtc is NULL by default). You could just set LockoutEnabled to true and if LockoutoutEndDateUtc is NULL the user is locked out indefinitely.
You would need to introduce your own flag into a custom IdentityUser-derived class and implement/enforce your own logic about enable/disable and preventing the user from logging in if disabled.
This all I did actually:
var lockoutEndDate = new DateTime(2999,01,01);
UserManager.SetLockoutEnabled(userId,true);
UserManager.SetLockoutEndDate(userId, lockoutEndDate);
Which is basically to enable lock out (if you don't do this by default already, and then set the Lockout End Date to some distant value.
Ozz is correct, however it may be adviseable to look at the base class and see if you can find a method that is checked for all signin angles - I think it might be CanSignIn?
Now that MS is open source you can see their implementation:
https://github.com/aspnet/AspNetCore/blob/master/src/Identity/src/Identity/SignInManager.cs
(Url has changed to:
https://github.com/aspnet/AspNetCore/blob/master/src/Identity/Core/src/SignInManager.cs)
public class CustomSignInManager : SignInManager<ApplicationUser>
{
public CustomSignInManager(UserManager<ApplicationUser> userManager,
IHttpContextAccessor contextAccessor,
IUserClaimsPrincipalFactory<ApplicationUser> claimsFactory,
IOptions<IdentityOptions> optionsAccessor,
ILogger<SignInManager<ApplicationUser>> logger,
IAuthenticationSchemeProvider schemes) : base(userManager, contextAccessor, claimsFactory, optionsAccessor, logger, schemes)
{
}
public override async Task<bool> CanSignInAsync(ApplicationUser user)
{
if (Options.SignIn.RequireConfirmedEmail && !(await UserManager.IsEmailConfirmedAsync(user)))
{
Logger.LogWarning(0, "User {userId} cannot sign in without a confirmed email.", await UserManager.GetUserIdAsync(user));
return false;
}
if (Options.SignIn.RequireConfirmedPhoneNumber && !(await UserManager.IsPhoneNumberConfirmedAsync(user)))
{
Logger.LogWarning(1, "User {userId} cannot sign in without a confirmed phone number.", await UserManager.GetUserIdAsync(user));
return false;
}
if (UserManager.FindByIdAsync(user.Id).Result.IsEnabled == false)
{
Logger.LogWarning(1, "User {userId} cannot sign because it's currently disabled", await UserManager.GetUserIdAsync(user));
return false;
}
return true;
}
}
Also consider overriding PreSignInCheck, which also calls CanSignIn:
protected virtual async Task<SignInResult> PreSignInCheck(TUser user)
{
if (!await CanSignInAsync(user))
{
return SignInResult.NotAllowed;
}
if (await IsLockedOut(user))
{
return await LockedOut(user);
}
return null;
}
You can use these classes... A clean implemantation of ASP.NET Identity...
It's my own code. int is here for primary key if you want different type for primary key you can change it.
IdentityConfig.cs
public class ApplicationUserManager : UserManager<ApplicationUser, int>
{
public ApplicationUserManager(IUserStore<ApplicationUser, int> store)
: base(store)
{
}
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var manager = new ApplicationUserManager(new ApplicationUserStore(context.Get<ApplicationContext>()));
manager.UserValidator = new UserValidator<ApplicationUser, int>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = true,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
};
manager.UserLockoutEnabledByDefault = false;
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider =
new DataProtectorTokenProvider<ApplicationUser, int>(
dataProtectionProvider.Create("ASP.NET Identity"));
}
return manager;
}
}
public class ApplicationSignInManager : SignInManager<ApplicationUser, int>
{
public ApplicationSignInManager(ApplicationUserManager userManager, IAuthenticationManager authenticationManager) :
base(userManager, authenticationManager) { }
public override Task<ClaimsIdentity> CreateUserIdentityAsync(ApplicationUser user)
{
return user.GenerateUserIdentityAsync((ApplicationUserManager)UserManager);
}
public static ApplicationSignInManager Create(IdentityFactoryOptions<ApplicationSignInManager> options, IOwinContext context)
{
return new ApplicationSignInManager(context.GetUserManager<ApplicationUserManager>(), context.Authentication);
}
}
public class ApplicationRoleManager : RoleManager<ApplicationRole, int>
{
public ApplicationRoleManager(IRoleStore<ApplicationRole, int> store)
: base(store)
{
}
}
public class ApplicationRoleStore : RoleStore<ApplicationRole, int, ApplicationUserRole>
{
public ApplicationRoleStore(ApplicationContext db)
: base(db)
{
}
}
public class ApplicationUserStore : UserStore<ApplicationUser, ApplicationRole, int,
ApplicationLogin, ApplicationUserRole, ApplicationClaim>
{
public ApplicationUserStore(ApplicationContext db)
: base(db)
{
}
}
IdentityModel.cs
public class ApplicationUser : IdentityUser<int, ApplicationLogin, ApplicationUserRole, ApplicationClaim>
{
//your property
//flag for users state (active, deactive or enabled, disabled)
//set it false to disable users
public bool IsActive { get; set; }
public ApplicationUser()
{
}
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser, int> manager)
{
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
return userIdentity;
}
}
public class ApplicationUserRole : IdentityUserRole<int>
{
}
public class ApplicationLogin : IdentityUserLogin<int>
{
public virtual ApplicationUser User { get; set; }
}
public class ApplicationClaim : IdentityUserClaim<int>
{
public virtual ApplicationUser User { get; set; }
}
public class ApplicationRole : IdentityRole<int, ApplicationUserRole>
{
public ApplicationRole()
{
}
}
public class ApplicationContext : IdentityDbContext<ApplicationUser, ApplicationRole, int, ApplicationLogin, ApplicationUserRole, ApplicationClaim>
{
//web config connectionStringName DefaultConnection change it if required
public ApplicationContext()
: base("DefaultConnection")
{
Database.SetInitializer<ApplicationContext>(new CreateDatabaseIfNotExists<ApplicationContext>());
}
public static ApplicationContext Create()
{
return new ApplicationContext();
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
}
}
I upvoted Watson, as there is another public method in SignInManager that accepts TUser user instead of string userName. The accepted answer only suggests overriding the method with the username signature. Both should really be overridden, otherwise there is a means of signing in a disabled user. Here are the two methods in the base implementation:
public virtual async Task<SignInResult> PasswordSignInAsync(string userName, string password, bool isPersistent, bool lockoutOnFailure)
{
var user = await UserManager.FindByNameAsync(userName);
if (user == null)
{
return SignInResult.Failed;
}
return await PasswordSignInAsync(user, password, isPersistent, lockoutOnFailure);
}
public virtual async Task<SignInResult> PasswordSignInAsync(User user, string password, bool isPersistent, bool lockoutOnFailure)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
var attempt = await CheckPasswordSignInAsync(user, password, lockoutOnFailure);
return attempt.Succeeded
? await SignInOrTwoFactorAsync(user, isPersistent)
: attempt;
}
Overriding CanSignIn seems like a better solution to me, as it gets called by PreSignInCheck, which is called in CheckPasswordSignInAsync. From what I can tell from the source, overriding CanSignIn should cover all scenarios. Here is a simple implementation that could be used:
public override async Task<bool> CanSignInAsync(User user)
{
var canSignIn = user.IsEnabled;
if (canSignIn) {
canSignIn = await base.CanSignInAsync(user);
}
return canSignIn;
}
In asp.net Core Identity v3, a new way of preventing a user from signing in has been added. Previously you could require that an account has a confirmed email address or phone number, now you can specify .RequireConfirmedAccount. The default implementation of the IUserConfirmation<> service will behave the same as requiring a confirmed email address, provide your own service to define what confirmation means.
public class User : IdentityUser<string>{
public bool IsEnabled { get; set; }
}
public class UserConfirmation : IUserConfirmation<User>
{
public Task<bool> IsConfirmedAsync(UserManager<User> manager, User user) =>
Task.FromResult(user.IsEnabled);
}
services.AddScoped<IUserConfirmation<User>, UserConfirmation>();
services.AddIdentity<User, IdentityRole>(options => {
options.SignIn.RequireConfirmedAccount = true;
} );
You need to implement your own UserStore to remove the identity.
Also this might help you.

How to change user password?

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);
}
...
}

Implementing Custom MembershipUser

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.

Resources