I have this seeder class which is called at the end of the Startup.cs file in the Configure method:
public class UserSeeder
{
private readonly ApplicationDbContext _context;
private readonly UserManager<ApplicationUser> _userManager;
public UserSeeder(ApplicationDbContext context, UserManager<ApplicationUser> userManager)
{
_context = context;
_userManager = userManager;
}
public async Task Seed()
{
if (!await _context.Users.AnyAsync())
{
var user = new ApplicationUser()
{
UserName = "admin",
Email = "admin#test.com"
};
await _userManager.CreateAsync(user, "passwort4admin");
}
}
}
The code is executed and I even put a try/catch around the method call but no error happens AND no user is inserted into the database!
Why not?
The problem is the complexity of the password. Add capital and numbers and symbols the issue will solve
Behind the scenes the UserManager<> uses a IUserStore did you configure this userstore in the startup.cs in the IOC container?
services.AddIdentity<ApplicationUser, ApplicationRole>()
.AddEntityFrameworkStores<MyContext, Guid>()
.AddUserStore<ApplicationUserStore>() //this one provides data storage for user.
.AddRoleStore<ApplicationRoleStore>()
.AddUserManager<ApplicationUserManager>()
.AddRoleManager<ApplicationRoleManager>()
.AddDefaultTokenProviders();
In my case, it was a space in the user name. It's possible that you have other constraints set up that make the user creation operation illegal.
In most cases, you can obtain explicit information on the exact cause of the error by investigating the message in the returned object from the operation.
IdentityUser userToBe = ...
IdentityResult result = await UserManager.CreateAsync(userToBe);
if(!result.Succeeded)
foreach(IdentityError error in result.Errors)
Console.WriteLine($"Oops! {error.Description} ({error.Code}));
I forgot to set "Username", it cannot be empty.
I set UserName=userEmail and it works
Related
I am using ASP.net core 2.0. I added a flag column called IsChangePassword to my AspNetUsers table and to my ApplicationUser class. The idea is to force the user to change their password. There is always a chance that they might enter a url to bypass being forced to change their password. I want to have it check that property every time a webpage is being loaded and redirect to ChangePassword if that flag is true.
You need a resource filter, which you'll need to inject with both UserManager<TUser> and IUrlHelperFactory. The former will obviously be used to check the value of IsChangePassword, while the latter will be necessary to check the current URL against your chosen redirect URL, to prevent an endless redirect loop. Simply:
public class ChangePasswordResourceFilter : IAsyncResourceFilter
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly IUrlHelperFactory _urlHelperFactory;
public ChangePasswordResourceFilter(UserManager<ApplicationUser> userManager, IUrlHelperFactory urlHelperFactory)
{
_userManager = userManager;
_urlHelperFactory = urlHelperFactory;
}
public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next)
{
var urlHelper = _urlHelperFactory.GetUrlHelper(context);
var redirectUrl = urlHelper.Page("~/PasswordChange");
var currentUrl = context.HttpContext.Request.Path;
if (redirectUrl != currentUrl)
{
var user = await _userManager.GetUserAsync(context.HttpContext.User);
if (user?.IsChangePassword ?? false)
{
context.Result = new RedirectResult(redirectUrl);
}
}
await next();
}
}
Then, in Startup.ConfigureServices:
services.AddScoped<ChangePasswordResourceFilter>();
...
services.AddMvc(o =>
{
o.Filters.Add(typeof(ChangePasswordResourceFilter));
});
I would use a middleware, in which I would check the HttpContext for the current principal and check the IsChangePassword property value of the underlying user.
Then, according to the IsChangePassword property value, I would redirect the current request to the change password form.
The pro of this solution is that you don't need to edit any actions and controllers.
The con is that you add a if statement to every requests but additional configuration is possible.
I am trying to work out if there is an easy way to get ASP.NET Core to log which [Authorize] attribute is failing. I have a mixture of "Role" and "Policy" authorize attributes but whenever a single one fails the logs just show:
Obviously this is the correct behaviour and it doesn't let someone in with incorrect permissions however if you have multiple attributes it's a bit of a pain to have to go and work out which one failed. If the log simply showed Authorization failed for Policy X then that would be really easy to then find what's failing.
Does anyone know if it's currently possible to make this happen through some option I'm unaware of?
EDIT: For example: If I had [Authorize(Policy = "Policy 1")] and [Authorize(Policy = "Policy 2")] and only "Policy 2" failed. I would like to see something that tells me that it was "Policy 2" that failed.
EDIT: For anyone still coming across this question this has now been implemented by Microsoft and is part of .NET 5.0, see issue https://github.com/aspnet/AspNetCore/issues/7789
For Roles and Policy, they are translated to requirements like RolesAuthorizationRequirement or your custom requirement like MinimumAgeRequirement.
For Authorization failed., this is logged by DefaultAuthorizationService in AuthorizeAsync, you may not able to get the exact name like Policy 1 and Policy 2. You could get the requirements for Policy.
Try to check whether workaround below meets your requirement.
Implement custom DefaultAuthorizationService
public class CustomAuthorizationService : DefaultAuthorizationService, IAuthorizationService
{
private readonly AuthorizationOptions _options;
private readonly IAuthorizationHandlerContextFactory _contextFactory;
private readonly IAuthorizationHandlerProvider _handlers;
private readonly IAuthorizationEvaluator _evaluator;
private readonly IAuthorizationPolicyProvider _policyProvider;
private readonly ILogger _logger;
public CustomAuthorizationService(IAuthorizationPolicyProvider policyProvider
, IAuthorizationHandlerProvider handlers
, ILogger<DefaultAuthorizationService> logger
, IAuthorizationHandlerContextFactory contextFactory
, IAuthorizationEvaluator evaluator
, IOptions<AuthorizationOptions> options)
: base(policyProvider, handlers, logger, contextFactory, evaluator, options)
{
_options = options.Value;
_handlers = handlers;
_policyProvider = policyProvider;
_logger = logger;
_evaluator = evaluator;
_contextFactory = contextFactory;
}
public new async Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, IEnumerable<IAuthorizationRequirement> requirements)
{
if (requirements == null)
{
throw new ArgumentNullException(nameof(requirements));
}
var authContext = _contextFactory.CreateContext(requirements, user, resource);
var handlers = await _handlers.GetHandlersAsync(authContext);
foreach (var handler in handlers)
{
await handler.HandleAsync(authContext);
if (!_options.InvokeHandlersAfterFailure && authContext.HasFailed)
{
break;
}
}
var result = _evaluator.Evaluate(authContext);
if (result.Succeeded)
{
_logger.LogInformation($"Authorization is succeeded for { JsonConvert.SerializeObject(requirements) }" );
//_logger.UserAuthorizationSucceeded();
}
else
{
//var r = result.Failure.FailedRequirements.Select(requirement => new { Requirement = requirement.GetType() });
var json = JsonConvert.SerializeObject(result.Failure.FailedRequirements);
_logger.LogInformation($"Authorization is failed for { json }");
//_logger.UserAuthorizationFailed();
}
return result;
}
}
Replace built-in DefaultAuthorizationService
services.AddAuthorization(config =>
{
config.AddPolicy("T1", policy => policy.AddRequirements(new MinimumAgeRequirement(21)));
});
services.Replace(ServiceDescriptor.Transient<IAuthorizationService, CustomAuthorizationService>());
Microsoft are implementing this by default in .NET 5.0, see the related GitHub issue for details and links to PR.
https://github.com/aspnet/AspNetCore/issues/7789
you can handle and log this inside Middlewares
public class AuthHandlerMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<ErrorHandlingMiddleware> _logger;
public AuthHandlerMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context, IHostingEnvironment env /* other scoped dependencies */)
{
await _next(context);
if (context.Response.StatusCode == 401)
_logger.LogInformation($"'{context.User.Identity.Name}' is unauthorized");
}
}
In your starup config,
public void Configure(IApplicationBuilder app, ... )
{
....
app.UseMiddleware<AuthHandlerMiddleware>();
}
I am using asp.net core mvc with asp.net identity.
My ApplicationUser has a property: Country => 'GB' or 'FR' etc...
I want to set the System.Threading.Thread.CurrentThread.CurrentCulture to the value read from the applicationUser.Country. to display all datetime/number values correctly in my views as set by the user.
Thus I created this action filter:
public class LanguageActionFilter : ActionFilterAttribute
{
private readonly ILogger _logger;
private UserManager<ApplicationUser> _userManager;
public LanguageActionFilter(ILoggerFactory loggerFactory, UserManager<ApplicationUser> userManager)
{
_logger = loggerFactory.CreateLogger("LanguageActionFilter");
_userManager = userManager;
}
public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var user = await _userManager.GetUserAsync(context.HttpContext.User);
Thread.CurrentThread.CurrentCulture = new CultureInfo(user.Country);
await base.OnActionExecutionAsync(context, next);
}
}
On the action/controller where this actionfilter is set I also run this code:
public async Task<IActionResult> Index()
{
var user = await this.userManager.GetUserAsync(User);
var todos = await service.GetTodosAsync(user.Id);
return View(todos);
}
At the moment I call the this.userManager.GetUserAsync(User);
TWO TIMES !
Then I thought about passing the user object to the
context.HttpContext.Items.Add("applicationUser", user);
and grab this user object again within the Index() action method:
var user = (ApplicationUser)base.HttpContext.Items["applicationUser"];
That worked, but is that a good idea? Do you see any problems with that approach?
What you did is fine...
I'm quoting from the definition of the HttpContext class :
/// <summary>
/// **Gets or sets a key/value collection that can be used to share data within the scope of this request.**
/// </summary>
public abstract IDictionary<object, object> Items { get; set; }
Edit : It seems ef core doesn't support first level cache. So my idea went for nothing
Your code works, but i think you won't gain considerable performance.
You probably use aspnet identity with entity framework. Since ef supports first level cache. , if you call this.userManager.GetUserAsync(User); more than one, ef retrieves user entity from database only once. As a result two ways approximately have same effect for performance. I wouldn't use HttpContext.Items for your case.
I am working on an application with authentication. I am using the following frameworks:
ASP.NET Core RC2
Entity Framework Core
The Npgsql EFCore driver
For seeding I created a DataSeeder class, that gets added as Transient Service in the ConfigureServices function and later called in the Configure function. It checks whether an IdentityUser exists, and if not it creates one called admin and adds the role Admin to it.
However, when adding the role to the user, I get an exception saying that a foreign key constraint gets violated and that the ASP_NET_USERS does not contain an entry with that PK.
I checked the database and it does indeed contain the new Role that I created, but the user table is still empty.
Here is my DataSeeder class:
public class DataSeeder {
private DatabaseContext _context;
private UserManager<IdentityUser> _userManager;
private RoleManager<IdentityRole> _roleManager;
public DataSeeder(DatabaseContext ctx, UserManager<IdentityUser> userManager, RoleManager<IdentityRole> roleManager) {
_context = ctx;
_userManager = userManager;
_roleManager = roleManager;
}
public async Task CreateUserAsync() {
if (! await _userManager.Users.AnyAsync()) {
var adminRole = new IdentityRole("admin");
var rootRole = new IdentityRole("root");
await _roleManager.CreateAsync(adminRole);
await _roleManager.CreateAsync(rootRole);
var user = new IdentityUser {
Email = "blabla#example.com",
EmailConfirmed = true,
UserName = "superadmin"
};
await _userManager.CreateAsync(user, "P#SSWORD");
await _userManager.AddClaimAsync(user, new Claim("role", "admin"));
}
}
}
So the exception is thrown at AddClaimAsync. Any help would be really appreciated
Check the result of _userManager.createAsync.
var result = await _userManager.CreateAsync(user, "P#SSWORD");
if (!result.Succeeded)
{
// something went wrong, inspect the errors
}
You'll most likely find a required property on your IdentityUser is missing or the password is not valid.
How would you seed users? I am following their documents here, but they only show how to seed data that is inserted directly by the ApplicationDbContext.
In the Account controller, the UserManager is created through DI. How would I instantiate a UserManager in the Seed method?
public class SeedData
{
public static void Initialize(IServiceProvider serviceProvider)
{
var context = serviceProvider.GetService<ApplicationDbContext>();
var userManager = serviceProvider.GetService<UserManager<ApplicationUser>>();
Then in Startup.cs in the Configure method:
SeedData.Initialize(app.ApplicationServices);
In the startup.cs in the configure method, you can add the following code to get the UserManager and from there seed the users.
var userManager = app.ApplicationServices.GetService<UserManager<ApplicationUser>>();
You would create a class in the lines of
public class UserSeed
{
private ApplicationDbContext _context;
private UserManager<ApplicationUser> _mgr;
public UserSeed(ApplicationDbContext context,UserManager<ApplicationUser> userManager)
{
_context = context;
_mgr = users;
}
public void UserSeedData()
{
var user = new ApplicationUser { UserName = "foo#foo.com", Email = "foo#foo.com" };
var result = _mgr.CreateAsync(user,"fooPassword");
......
......
}
}
In the configure method of your startup.cs take userSeed through DI like
public async void Configure(...., UserSeed userSeedData)
and then inside the method you call
userSeedData.UserSeedData();
Don't use this code as is, you would probably want to check if the user already exists before you create it.
Note: This method will run once every time your application starts.