Writing a custom IUserPasswordStore and SignInManager.PasswordSignInAsync in Identity 2.1 - asp.net-identity-2

Building a custom IUserPasswordStore to connect to a legacy system's username/password table. The password is hashed with custom code in the table so I need to write custom code for PasswordSignInAsync.
Do I need to override PasswordSignInAsync or is there a method I can provide that just does the hashing of the password? If I do override the entire PasswordSignInAsync is there sample code somewhere showing me what needs to be done in the method?

That was easier than I thought.
Override CheckPasswordAsync in UserManager.

For someone who wants to see the complete setup in .NET 6, this is how it looks like:
Step 1:
Add CustomUserManager to override CheckPasswordAsync:
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
public class CustomUserManager<TUser> : UserManager<TUser> where TUser : IdentityUser
{
public CustomUserManager(IUserStore<TUser> store, IOptions<IdentityOptions> optionsAccessor,
IPasswordHasher<TUser> passwordHasher, IEnumerable<IUserValidator<TUser>> userValidators,
IEnumerable<IPasswordValidator<TUser>> passwordValidators, ILookupNormalizer keyNormalizer,
IdentityErrorDescriber errors, IServiceProvider services, ILogger<UserManager<TUser>> logger)
: base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer,
errors, services, logger)
{
}
// THIS IS ONLY CALLED FOR USERS STORED IN YOUR IDENTITY DATABASE
public override Task<bool> CheckPasswordAsync(TUser user, string password)
{
// Add custom check using user.UserName and password
return Task.FromResult(true); // Replace this with your custom check
}
}
Step 2:
Register it in your Program.cs
builder.Services
.AddDefaultIdentity<ApplicationUser>(options =>
{
options.SignIn.RequireConfirmedAccount = false;
})
.AddUserManager<CustomUserManager<ApplicationUser>>() <----- THIS GUY
.AddEntityFrameworkStores<ApplicationDbContext>();
ApplicationUser and ApplicationDbContext look like this:
public class ApplicationUser : IdentityUser
{
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
}
}
Step 3:
Try to Login using this:
var result = await _signInManager.PasswordSignInAsync("SomeUserNameInYourIdentityDatabase", "SomePassword", isPersistent: true, lockoutOnFailure: false);

Related

Is it good way for protecting the form model in Asp.Net Core MVC using DataProtection

I have tried create the Asp.Net Core MVC app where I want to protect the form model especially Id.
I have found the posibility with DataProtection with method - Protect and Unprotect string.
I've used this implementation:
public class HomeController : Controller
{
readonly IDataProtector _protector;
private readonly IUserRepository _userRepository;
public HomeController(IDataProtectionProvider provider, IUserRepository userRepository)
{
_protector = provider.CreateProtector("DataProtectionDemo.Controllers.HomeController");
_userRepository = userRepository;
}
[HttpGet]
public async Task<IActionResult> Index(int id)
{
var user = await _userRepository.GetUserDetail(id);
user.Id = _protector.Protect(user.Id);
return View(user);
}
[HttpPost]
public async Task<IActionResult> Index(UserViewModel model)
{
try
{
model.Id = _protector.Unprotect(model.Id);
await _userRepository.SaveUser(model);
return RedirectToAction(nameof(Index));
}
catch (Exception e)
{
model.Error = e.Message;
return View(model);
}
}
In this case I want to protect UserId in hidden field with encrypted string, but I don't know if this using of Dataprotection is correct way. I know of posibilities around Authorization Policy and it might be next step check user permission but I am wondering about this additional way as create better protection.
Is it good way how protect the form model?
I have opened this issue on Github in DataProtection topic:
https://github.com/aspnet/DataProtection/issues/278
The answer was that this solution should work fine.

UserId not found error in aspnet Identity at GenerateUserIdentityAsync method

I am getting UserId not found error after registring a user and also after login.Moreoever, after registration, data is saved to database and in dbo.AspNetUsers table, id column is auto incremented and return type is int.
There is UserId Column in AspNetUserClaims table.It has 4 Col---Id,UserId,ClaimType,ClaimValue.It has Id column as auto incremented not the userId.
I was initially successfully changed Primary key from string to int by following this link---http://www.asp.net/identity/overview/extensibility/change-primary-key-for-users-in-aspnet-identity.
It was running succesfully before but now it is giving me error at this line---
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser, int> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
Exception Details: System.InvalidOperationException: UserId not found.
This is the complete stack trace. you can see it here----http://pastebin.com/0hp5eAnp
It was working fine earlier but now when i added foreign key relationship with other tables, i don't know what is missing there. In the database all the tables are created properly with proper relationship between them but something is missing here.
My ApplicationUser class is something like this-------
public class ApplicationUser : IdentityUser<int, CustomUserLogin, CustomUserRole, CustomUserClaim>
{
public ApplicationUser()
{
this.Posts = new HashSet<Post>();
}
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public virtual ICollection<Post> Posts { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser, int> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
public class CustomUserRole : IdentityUserRole<int> { }
public class CustomUserClaim : IdentityUserClaim<int> { }
public class CustomUserLogin : IdentityUserLogin<int> { }
public class CustomRole : IdentityRole<int, CustomUserRole>
{
public CustomRole() { }
public CustomRole(string name) { Name = name; }
}
public class CustomUserStore : UserStore<ApplicationUser, CustomRole, int,
CustomUserLogin, CustomUserRole, CustomUserClaim>
{
public CustomUserStore(ApplicationDbContext context)
: base(context)
{
}
}
public class CustomRoleStore : RoleStore<CustomRole, int, CustomUserRole>
{
public CustomRoleStore(ApplicationDbContext context)
: base(context)
{
}
}
and my IdentityConfig.cs class file is something like this-------
// Configure the application user manager used in this application. UserManager is defined in ASP.NET Identity and is used by the application.
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 CustomUserStore(context.Get<ApplicationDbContext>()));
// Configure validation logic for usernames
manager.UserValidator = new UserValidator<ApplicationUser, int>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
// Configure validation logic for passwords
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 1,
//RequireNonLetterOrDigit = true,
//RequireDigit = true,
//RequireLowercase = true,
//RequireUppercase = true,
};
// Configure user lockout defaults
manager.UserLockoutEnabledByDefault = true;
manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
manager.MaxFailedAccessAttemptsBeforeLockout = 5;
// Register two factor authentication providers. This application uses Phone and Emails as a step of receiving a code for verifying the user
// You can write your own provider and plug it in here.
manager.RegisterTwoFactorProvider("Phone Code", new PhoneNumberTokenProvider<ApplicationUser, int>
{
MessageFormat = "Your security code is {0}"
});
manager.RegisterTwoFactorProvider("Email Code", new EmailTokenProvider<ApplicationUser, int>
{
Subject = "Security Code",
BodyFormat = "Your security code is {0}"
});
manager.EmailService = new EmailService();
manager.SmsService = new SmsService();
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider =
new DataProtectorTokenProvider<ApplicationUser, int>(dataProtectionProvider.Create("ASP.NET Identity"));
}
return manager;
}
}
// Configure the application sign-in manager which is used in this application.
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);
}
}
i have seen many stackoverflow answers but not getting it to work.Can someone plzz plzz see what is missing, what should i do now.thanks in advance.
Here, in the applicationUser class, at the Id column, it showing some warning and message in tooltip like this-------
models.ApplicationUSer.ID hides inherited member
Microsoft.Aspnet.Identity.EntityFramework.IDentity
USer.Id. To make current member override
that implementation, add override keyword otherwise
add new keyword where x is just the namespace.
My StartUp.Auth.cs in App_Start folder is like this------
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
// Configure the sign in cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser, int>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentityCallback: (manager, user) => user.GenerateUserIdentityAsync(manager), getUserIdCallback:(id)=>(id.GetUserId<int>()))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Enables the application to temporarily store user information when they are verifying the second factor in the two-factor authentication process.
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
// Enables the application to remember the second login verification factor such as phone or email.
// Once you check this option, your second step of verification during the login process will be remembered on the device where you logged in from.
// This is similar to the RememberMe option when you log in.
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
// Uncomment the following lines to enable logging in with third party login providers
//app.UseMicrosoftAccountAuthentication(
// clientId: "",
// clientSecret: "");......................................................................
and my startUp.cs file is like this----
[assembly: OwinStartupAttribute(typeof(WebApp.Startup))]
namespace WebApp
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
}
}
You will have to pull out your ApplicationUserManager to have it nice and clean and implement more methods... For example see following post (It implemented all methods with your custom Key (TKey in the example):
http://www.symbolsource.org/MyGet/Metadata/aspnetwebstacknightly/Project/Microsoft.AspNet.Identity.Core/2.0.0-beta1-140211/Release/Default/Microsoft.AspNet.Identity.Core/Microsoft.AspNet.Identity.Core/UserManager.cs?ImageName=Microsoft.AspNet.Identity.Core
You will see that the error you receive GetSecurityStampAsync also is implemented there.

web api 2 - Passing data from action filter to action as an argument

In order to avoid getting the user data on every action I've create an custom action filter that gets the user by its ID and then passes to the action.
public class UserDataAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
...
// getting the user and storing it in the request properties
object user = userBLL.GetUserById(userId);
actionContext.Request.Properties.Add("User", user);
}
}
And the I can get the user object in the action method like this:
[Authorize]
[UserData]
[HttpGet]
[Route("dosomething")]
public IHttpActionResult DoSomething()
{
// retrieve the user
object user;
Request.Properties.TryGetValue("User", out user);
User u = (User)user;
return Ok();
}
However, in MVC it's possible to use ActionParameters in the filter to store something that will be used by the action method, like so:
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
...
// Create object parameter.
filterContext.ActionParameters["User"] = userBLL.GetUserById(userId);
}
And then use the User object as if it were part of the original request:
[AddActionParameter]
public ActionResult Index(User user)
{
// Here I can access the user setted on the filter
...
return View();
}
So, my question is: There is a way in Web API 2 to pass the User object from the action filter to the action as an argument, just like in MVC?
With ASP.NET Web API, you can create a parameter binding to receive an object, User in your case. You don't have to create a filter for this. So, you will create a binding like this.
public class UserParameterBinding : HttpParameterBinding
{
public UserParameterBinding(HttpParameterDescriptor descriptor) :
base(descriptor) { }
public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider,
HttpActionContext context,
CancellationToken cancellationToken)
{
SetValue(context, new User() { // set properties here });
return Task.FromResult<object>(null);
}
}
Then, to use the binding, you will configure it, like this.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// snip
config.ParameterBindingRules.Insert(0, d =>
d.ParameterType == typeof(User) ? new UserParameterBinding(d) : null);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
With that, wherever you have User as action method parameter, it will automatically bind the instance you are creating inside UserParameterBinding to that parameter.

Injecting HttpContext.Current in MVC Role Provider

I have a class in my MVC5 application that deals with some user related functionality and has a dependency on HttpContext.Current.User as shown below
public interface IUser
{
// return roles of currently logged in user
string[] GetRoles;
}
public Class User : IUser
{
private HttpContext context;
// constructor
public User(HttpContext user)
{
this.context = user
}
// get roles
public string[] GetRoles()
{
string username = this.context.User.Identity.Name;
// get roles through some DB calls
string[] roles = someDbCalls();
return roles;
}
}
I have it setup for dependency injection using Ninject in NinjectWebCommon.cs as
kernel.Bind<IUser>().To<User>().WithConstructorArgument("user", x => HttpContext.Current);
This works fine if called from anywhere in my code except in my custom RolesProvider which is setup as shown below
public class CustomRoleProvider : RoleProvider
{
[Inject]
public IUser user {get; set;}
public override string[] GetRolesForUser(string username)
{
return this.user.GetRoles();
}
}
The call to GetRoles() from my custom role provider fails because HttpContext.Current.User injected by Ninject under this case is null. Any idea on what I may be doing wrong?
Edit:
On further testing, it appears that the problem is with the way I am using Ninject in my custom Roles provider. Using the attribute injection as shown below
[Inject]
public IUser user {get; set;}
works only the first time and subsequent calls fail with HttpContext.Current.User is null error. I have fixed it in a hacky way by forcing the injection to happen each time I call the GetRoles method as shown below
public class CustomRoleProvider : RoleProvider
{
private IUser user;
public override string[] GetRolesForUser(string username)
{
// force ninject to inject a new instance of my interface
var user = DependencyResolver.Current.GetService<IUser>();
return user.GetRoles();
}
}
Not sure why this works and so I am leaving this question open if someone can provide an explanation.
It appears that by the time the role provider is called, the HTTPContext.Current is not yet set. This leads to other issues with custom RolesProvider (like the Null Reference Exception due to EtwTracing bug see: SqlRoleProvider on IIS8 Express
).
If you really need the HTTPContext.Current instead of using the Thread's PrincipalIdentity, you can setup your app to use compatibility mode. This appears to resolve the problem by setting up the HttpContext.Current sooner:
https://social.msdn.microsoft.com/Forums/en-US/8ee88c92-5e8a-4c66-ace7-887eb500e1cb/httpcontextcurrent-always-been-null

Is WCF Service EntitySetRights.AllRead Secure?

I have the following code inside MyDataService.svc.cs (This is an example from DevExpress):
namespace MyDataService {
[System.ServiceModel.ServiceBehavior(IncludeExceptionDetailInFaults = true)]
[JSONPSupportBehavior]
public class DataService : DataService<TestDataEntities>, IServiceProvider {
public static void InitializeService(DataServiceConfiguration config) {
config.SetEntitySetAccessRule("*", EntitySetRights.AllRead);
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3;
}
public object GetService(Type serviceType) {
if (serviceType == typeof(IDataServiceStreamProvider)) {
return new ImageStreamProvider();
}
return null;
}
protected override void OnStartProcessingRequest(ProcessRequestArgs args) {
CustomBasicAuth.Authenticate(HttpContext.Current);
if (HttpContext.Current.User == null)
throw new DataServiceException(401, "Invalid login or password");
base.OnStartProcessingRequest(args);
}
}
}
So while this is will check the Entity for a username and password, how safe is it that config.SetEntitySetAccessRule is set to AllRead. Wouldn't someone just be able to see this information on a url such as www.website.com/MyDataService.svc/Customer (where Customer is the table). If this is not so can someone please fill in the conceptual gap I am facing. Thanks!
You are correct that all entities will be returned when queried - AllRead just disallows insert updates and deletes.
You will need to use Query Interceptor to add your logic to restrict users to the set of data they have permission to view, for example adding a check user id to the query.

Resources