Unable to resolve service for type Microsoft.AspNetCore.Identity.RoleManager for ASP.Net Core 2.0 - asp.net-core

How do I inject RoleManager in ASP.Net Core 2.0?
I'm getting the following error:
Unable to resolve service for type
Microsoft.AspNetCore.Identity.RoleManager for ASP.Net Core 2.0
I have the following code:
// add identity
var builder = services.AddIdentityCore<AppUser>(o =>
{
// configure identity options
o.Password.RequireDigit = false;
o.Password.RequireLowercase = false;
o.Password.RequireUppercase = false;
o.Password.RequireNonAlphanumeric = false;
o.Password.RequiredLength = 6;
});
builder = new IdentityBuilder(builder.UserType, typeof(AppRole), builder.Services);
builder.AddSignInManager<SignInManager<AppUser>>();
AppUser and AppRole are derived from IdentityUser and IdentityRole, respectively.

I'm not sure why you are using AddIdentityCore(). Have you tried the conventional AddIdentity():
services.AddIdentity<AppUser, AppRole>(options =>
{
...
});

Related

I cannot Access the currently logged in user in my controllers actions (Blazor ASP.NET Core hosted .NET 6 and IdentityServer JWT)

I created a Hosted ASP.NET core Blazor WASM app using the Individual Authentication VS 2022 template which also includes Duende Identity Server configuration, apart from having to switch from Blazor UI to .cshtml for authentication views, it has done what is expected of it. A big problem I'm having is that I can't seem to access the currently logged in user from within my controllers neither using the User property from ControllerBase, nor via the IHttpContextAccessor, the Claims Principle instances all seem to be null when inspected via the debugging mode, meanwhile on the client-side WASM I can access my subject Id, email or whatever I specify in the ProfileService just fine (Which is not really useful unless I would want to fetch the user from the server via the subject ID sent from the client using a parameter or something... which would be disastrous I know..)
Here's my Client/Program.cs:
using System.Globalization;
using CurrieTechnologies.Razor.SweetAlert2;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.JSInterop;
using Proj.Client;
using Proj.Client.Auth;
using Proj.Client.Helpers;
using Proj.Client.Repository;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
var services = builder.Services;
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after ");
services.AddHttpClient<IHttpService>("Proj.ServerAPI",
client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
// Supply HttpClient instances that include access tokens when making requests to the server project
services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("Proj.ServerAPI"));
// SERVICES
services.AddOptions();
services.AddLocalization();
// builder.Services.AddScoped<DialogService>();
// builder.Services.AddScoped<NotificationService>();
// builder.Services.AddScoped<TooltipService>();
// builder.Services.AddScoped<ContextMenuService>();
services.AddApiAuthorization()
.AddAccountClaimsPrincipalFactory<CustomUserFactory>();
var host = builder.Build();
var js = host.Services.GetRequiredService<IJSRuntime>();
var culture = await js.InvokeAsync<string>("getFromLocalStorage", "culture");
CultureInfo selectedCulture;
selectedCulture = culture == null ? new CultureInfo("en") : new CultureInfo(culture);
CultureInfo.DefaultThreadCurrentCulture = selectedCulture;
CultureInfo.DefaultThreadCurrentUICulture = selectedCulture;
await host.RunAsync();
And the Server/Program.cs:
using System.IdentityModel.Tokens.Jwt;
using Duende.IdentityServer.Services;
using Microsoft.AspNetCore.ApiAuthorization.IdentityServer;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.EntityFrameworkCore;
using Proj.Server.Data;
using Proj.Server.DbInitializer;
using Proj.Server.Models;
using Proj.Server.Services;
using Proj.Server.Utils;
var builder = WebApplication.CreateBuilder(args);
var services = builder.Services;
// Add services to the container.
var connectionString = builder.Configuration.GetConnectionString("MySQLConnectionLocal");
var serverVersion = new MySqlServerVersion(ServerVersion.AutoDetect(connectionString));
services.AddDbContext<ApplicationDbContext>(
dbContextOptions => dbContextOptions
.UseMySql(connectionString, serverVersion)
// The following three options help with debugging, but should
// be changed or removed for production.
.LogTo(Console.WriteLine, LogLevel.Information)
.EnableSensitiveDataLogging()
.EnableDetailedErrors()
);
services.AddAutoMapper(typeof(MappingConfig));
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddDefaultIdentity<ApplicationUser>(options =>
{
options.SignIn.RequireConfirmedAccount = true;
options.Stores.MaxLengthForKeys = 80;
options.User.RequireUniqueEmail = true;
options.Tokens.ProviderMap.Add("CustomEmailConfirmation",
new TokenProviderDescriptor(
typeof(CustomEmailConfirmationTokenProvider<ApplicationUser>)));
options.Tokens.EmailConfirmationTokenProvider = "CustomEmailConfirmation";
})
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddTransient<CustomEmailConfirmationTokenProvider<ApplicationUser>>();
services.AddTransient<IEmailSender, EmailSender>();
services.Configure<AuthMessageSenderOptions>(builder.Configuration);
services.AddHttpContextAccessor();
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>()
.AddDeveloperSigningCredential();
builder.Services.AddTransient<IProfileService, ProfileService>();
builder.Services.AddTransient<IFileStorageService, InAppStorageService>();
builder.Services.AddDataProtection();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");
services.AddAuthentication()
.AddIdentityServerJwt()
.AddGoogle(googleOptions =>
{
googleOptions.ClientId = builder.Configuration["Authentication:Google:ClientId"];
googleOptions.ClientSecret = builder.Configuration["Authentication:Google:ClientSecret"];
}).AddFacebook(facebookOptions =>
{
facebookOptions.AppId = builder.Configuration["Authentication:Facebook:AppId"];
facebookOptions.AppSecret = builder.Configuration["Authentication:Facebook:AppSecret"];
});
services.Configure<DataProtectionTokenProviderOptions>(o =>
o.TokenLifespan = TimeSpan.FromHours(3));
services.ConfigureApplicationCookie(o =>
{
o.ExpireTimeSpan = TimeSpan.FromDays(5);
o.SlidingExpiration = true;
});
services.AddControllersWithViews();
services.AddRazorPages();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
app.UseWebAssemblyDebugging();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseBlazorFrameworkFiles();
app.UseStaticFiles();
app.UseRouting();
app.UseIdentityServer();
app.UseAuthentication();
app.UseAuthorization();
app.MapRazorPages();
app.MapControllers();
app.MapFallbackToFile("index.html");
app.Run();
And I think that the ProfileService Impelementation would also be Useful:
using IdentityModel;
using Duende.IdentityServer.Models;
using Duende.IdentityServer.Services;
namespace Proj.Server.Services;
public class ProfileService : IProfileService
{
public ProfileService()
{
}
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var nameClaim = context.Subject.FindAll(JwtClaimTypes.Name);
context.IssuedClaims.AddRange(nameClaim);
var subClaim = context.Subject.FindAll(JwtClaimTypes.Subject);
context.IssuedClaims.AddRange(subClaim);
var roleClaims = context.Subject.FindAll(JwtClaimTypes.Role);
context.IssuedClaims.AddRange(roleClaims);
await Task.CompletedTask;
}
public async Task IsActiveAsync(IsActiveContext context)
{
await Task.CompletedTask;
}
}
I'd be grateful if anyone could point me in the right direction,
Thanks :)
Set the options in AddApiAuthorization :
builder.Services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
{
const string OpenId = "openid";
options.IdentityResources[OpenId].UserClaims.Add(JwtClaimTypes.Email);
options.ApiResources.Single().UserClaims.Add(JwtClaimTypes.Email);
options.IdentityResources[OpenId].UserClaims.Add(JwtClaimTypes.Id);
options.ApiResources.Single().UserClaims.Add(JwtClaimTypes.Id);
options.IdentityResources[OpenId].UserClaims.Add(JwtClaimTypes.Name);
options.ApiResources.Single().UserClaims.Add(JwtClaimTypes.Name);
options.IdentityResources[OpenId].UserClaims.Add(JwtClaimTypes.Role);
options.ApiResources.Single().UserClaims.Add(JwtClaimTypes.Role);
});
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");
In your controller:
var name = User.FindFirst(ClaimTypes.Name)?.Value;
Turns out this question also describes the same problem I have, By removing
.AddIdentityServerJwt()
From the line
services.AddAuthentication()
.AddIdentityServerJwt()
Everything started working as expected...
Now being the beginner I am, I'm not certain whether or not I should've commented out that line, it may have been important, so if anyone knows a better solution by all means share...
Thanks.

OpenIddict Console Application - Get All Application Clients - ListSync Fails

I have written a simple console application to get all application clients from OpenIddict server. I tried all the possibilities and getting the syntax error. The code is below. I did not find any example in Github and found some outdated example (2017) is no longer relevant now. Please help
public static async Task<bool> Test()
{
var services = CreateServices();
var provider = services.BuildServiceProvider();
var scope = provider.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<CustomDbContext>();
await context.Database.EnsureCreatedAsync();
var manager = scope.ServiceProvider.GetRequiredService<IOpenIddictApplicationManager>();
var result = await manager.FindByClientIdAsync("TestApp"); // It Works
IQueryable<OpenIddictEntityFrameworkCoreApplication> _applicationsQuery = Enumerable.Empty<OpenIddictEntityFrameworkCoreApplication>().AsQueryable();
_applicationsQuery.Where(apps => apps.ClientId != "");
var clients = manager.ListAsync<Func<OpenIddictEntityFrameworkCoreApplication>>(_applicationsQuery); //Compiler Error
return (result != null);
}
private static IServiceCollection CreateServices()
{
var services = new ServiceCollection();
services.AddDbContext<CustomDbContext>(opts =>
{
opts.UseSqlServer(
ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString,
b => b.MigrationsAssembly("Program"));
opts.UseOpenIddict();
});
services.AddOpenIddict() // Register the OpenIddict core components.
.AddCore(options =>
{
// Configure OpenIddict to use the Entity Framework Core stores and models.
// Note: call ReplaceDefaultEntities() to replace the default OpenIddict entities.
options.UseEntityFrameworkCore()
.UseDbContext<CustomDbContext>();
// Enable Quartz.NET integration.
options.UseQuartz();
});
return services;
}
ListAsync() returns an IAsyncEnumerable<T> collection, so you can use await foreach to iterate the collection:
await foreach (var application in manager.ListAsync())
{
Console.WriteLine(await manager.GetClientIdAsync(application));
}
You can also reference the System.Linq.Async package and use the async LINQ extensions. For instance, here's how you could retrieve all the client identifiers of all existing applications:
var identifiers = await manager.ListAsync()
.SelectAwait(application => manager.GetClientIdAsync(application))
.ToListAsync();

DI Registration service type .net core 3.0

I have one n-tier layered app and in Infrastructure module where I'm trying to develop sending email for confirmation an user, I'm getting an error.
No service for type
'IMS.Infrastructure.Helpers.CustomEmailConfirmationTokenProvider`1[Microsoft.AspNetCore.Identity.IdentityUser]'
has been registered.
From code what I had did is next:
public class CustomEmailConfirmationTokenProvider<TUser> : DataProtectorTokenProvider<TUser> where TUser : class
{
public CustomEmailConfirmationTokenProvider(IDataProtectionProvider dataProtectionProvider, IOptions<DataProtectionTokenProviderOptions> options, ILogger<DataProtectorTokenProvider<TUser>> logger) : base(dataProtectionProvider, options)
{
}
}
and for creating services:
services.AddDbContext<ApplicationIdentityDbContext>(options =>
options.UseSqlServer(configuration.GetConnectionString("Default")));
services.AddIdentityCore<ApplicationUser>(options =>
{
options.Password.RequireDigit = false;
options.Password.RequireLowercase = false;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
options.Password.RequiredLength = 4;
options.SignIn.RequireConfirmedEmail = true;
options.Tokens.ProviderMap.Add("CustomEmailConfirmation",
new TokenProviderDescriptor(
typeof(CustomEmailConfirmationTokenProvider<IdentityUser>)));
options.Tokens.EmailConfirmationTokenProvider = "CustomEmailConfirmation";
})
.AddEntityFrameworkStores<ApplicationIdentityDbContext>();
services.AddTransient<CustomEmailConfirmationTokenProvider<ApplicationUser>>(o =>
{
var service = new CustomEmailConfirmationTokenProvider<ApplicationUser>(o.GetService<IDataProtectionProvider>(), o.GetService<IOptions<DataProtectionTokenProviderOptions>>(), o.GetService<ILogger<DataProtectorTokenProvider<ApplicationUser>>>());
return service;
});
I will need help to understand how service CustomEmailConfirmationTokenProvider is not registered, what I had did wrong ?
Kind Regards,
Danijel
From perspective of IoC container CustomEmailConfirmationTokenProvider<ApplicationUser> and CustomEmailConfirmationTokenProvider<IdentityUser> are two different and unrelated classes.
You should have both usage and registration to have same type of user.

Asp Net Core Identity UserManager change context

I have implemented the microsoft identity framework, I'm using the UserManager via dependecy injection, since it is a multi-tenancy project I would need to extend the UserManager class to pass the desired context, I searched on google but I could not find or better adapt anything For my case.
_userManagerRepository = new UserManagerRepository(new PortaleContext(tenantContext.Tenant))
_userMgr = new UserManager<ApplicationUser>(userStore,null,null,null,null,null,null,null,null);
But when I run this method:
var passwordResetToken = await _userMgr.GeneratePasswordResetTokenAsync(user)
I get the following error:
<div class="titleerror">NotSupportedException: No IUserTokenProvider named 'Default' is registered.</div>
<p class="location">Microsoft.AspNetCore.Identity.UserManager+<VerifyUserTokenAsync>d__122.MoveNext()</p>
And this is a part of my startup class:
services.AddIdentity<ApplicationUser, IdentityRole>(config =>
{
config.User.RequireUniqueEmail = true;
config.Password.RequiredLength = 8;
// config.Cookies.ApplicationCookie.LoginPath = "/App/Login";
config.SignIn.RequireConfirmedEmail = false;
config.SignIn.RequireConfirmedPhoneNumber = false;
})
.AddEntityFrameworkStores<AdminContext>()
.AddDefaultTokenProviders();
services.AddIdentity<ApplicationUser, IdentityRole>(config =>
{
config.User.RequireUniqueEmail = true;
config.Password.RequiredLength = 8;
config.Password.RequireNonAlphanumeric = false;
config.Password.RequireUppercase = false;
// config.Cookies.ApplicationCookie.LoginPath = "/App/Login";
config.SignIn.RequireConfirmedEmail = false;
config.SignIn.RequireConfirmedPhoneNumber = false;
})
.AddEntityFrameworkStores<PortaleContext>()
.AddDefaultTokenProviders();
In a nutshell, depending on which address the request arrives, the usermanager must update to its context
This article references an error when calling AddDefaultTokenProviders() Twice. This may be your issue, I am looking for a similar solution but this one didn't work for me, but maybe you haven't seen it yet and it works for you.
https://github.com/aspnet/Identity/issues/972

Resolve dependency on token validation and on controller constructor

I'm trying to share a security object through an application using dependency injection:
services.AddScoped<IRequesterFilter, RequesterFilter>();
The object is populated when a token is validated:
app.UseJwtBearerAuthentication(options =>
{
options.AutomaticAuthenticate = true;
options.Authority = "https://xxxxxx.xxx";
options.TokenValidationParameters.ValidateAudience = false;
options.TokenValidationParameters.ValidIssuer = "https://yyyyyy.yyyy";
options.Events = new JwtBearerEvents
{
OnValidatedToken = context =>
{
var requestFilter = context.HttpContext.ApplicationServices.GetService<IRequesterFilter>();
requestFilter.RequesterLevel = RequesterLevelEnum.Client;
requestFilter.AppId = context.AuthenticationTicket.Principal.FindFirst("appid").Value;
return Task.FromResult(0);
}
};
});
But when I get IRequesterFilter from the controller constructor, the object is not initialized:
public ValuesController(IRequesterFilter requestFilter)
{
var x = requestFilter;
}
I'm using ASP.NET Core 1.0 RC1.
You are using the wrong container.
You are using context.HttpContext.ApplicationServices.GetService<IRequesterFilter>(), which is for application managed/application-wide objects, usually singletons.
For scoped services you have to use the scoped container (called RequestServices), which is would be context.HttpContext.RequestServices.GetService<IRequesterFilter>().
Please note that ApplicationServices will be removed from HttpContext with ASP.NET Core RC2, which will be a breaking change in the way are using it right now.