AspNetCore unable to resolve service - c#

I'm adding Microsoft.AspNetCore.Identity to a project, and I get
InvalidOperationException: Unable to resolve service for type 'Microsoft.AspNetCore.Identity.SignInManager'1[Web.Security.Entities.IUser'1[System.Int32]]' while attempting to activate 'Web.Security.Services.SecurityService'2[Web.Security.Entities.IUser'1[System.Int32],System.Int32]'.
Exception is copypasted from postman, it encoded some symbols.
Here's my Startup.cs:
public class Startup
{
ServiceProvider serviceProvider;
IConfigurationRoot configurationRoot;
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
configurationRoot = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: false)
.AddIniFile("3CXPhoneSystem.ini")
.Build();
}
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
serviceProvider = services
.AddLogging((builder) => builder.SetMinimumLevel(LogLevel.Trace))
.AddSingleton<ISecurityService, SecurityService<IUser<int>, int>>()
.AddSingleton<ITaskService, TaskService>()
.AddTransient<IEmailSender, EmailSender>()
.AddSingleton<ITranslation, Translation>()
.BuildServiceProvider();
services.AddDbContext<SecurityDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("dataContext")));
services.AddIdentity<Web.Security.Entities.User<int>, IdentityRole>()
.AddEntityFrameworkStores<SecurityDbContext>()
.AddDefaultTokenProviders();
services.AddCors(o => o.AddPolicy("CorsPolicy", builder =>
{
builder
.AllowAnyMethod()
.AllowAnyHeader()
.WithOrigins("http://localhost:52170");
}));
services.AddMvc();
services.Configure<IdentityOptions>(options =>
{
// Password settings
options.Password.RequireDigit = true;
options.Password.RequiredLength = 8;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = true;
options.Password.RequireLowercase = false;
options.Password.RequiredUniqueChars = 6;
// Lockout settings
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
options.Lockout.MaxFailedAccessAttempts = 10;
options.Lockout.AllowedForNewUsers = true;
// User settings
options.User.RequireUniqueEmail = true;
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseDefaultFiles();
app.UseStaticFiles();
app.Use(async (context, next) =>
{
await next();
if (context.Response.StatusCode == 404 && !Path.HasExtension(context.Request.Path.Value) && !context.Request.Path.Value.StartsWith("api"))
{
context.Response.Redirect("/");
}
});
app.UseMvc(routes =>
{
routes.MapRoute(name: "DefaultApi", template: "api/{controller}/{action}/{id?}");
routes.MapRoute("RouteToAngular", "{*url}", defaults: new { controller = "Route", action = "Index" });
});
app.UseAuthentication();
app.UseCors("CorsPolicy");
}
Here, SecurityService is a class that will handle registration/login/other identity related requests, it looks like this
public class SecurityService<TUser, TKey> : ISecurityService where TUser : class, IUser<TKey>
{
SignInManager<TUser> signInManager;
IConfiguration configuration;
private readonly IHttpContextAccessor httpContextAccessor;
UserManager<TUser> userManager;
IEmailSender emailSender;
IUrlHelper urlHelper;
ISession session;
ILogger<SecurityService<TUser, TKey>> logger;
ITranslation translation;
public SecurityService(
SignInManager<TUser> signInManager,
UserManager<TUser> userManager,
IConfiguration configuration,
IHttpContextAccessor httpContextAccessor,
IEmailSender emailSender,
IUrlHelper urlHelper,
ISession session,
ILogger<SecurityService<TUser, TKey>> logger,
ITranslation translation)
{
this.signInManager = signInManager;
this.userManager = userManager;
this.configuration = configuration;
this.httpContextAccessor = httpContextAccessor;
this.urlHelper = urlHelper;
this.session = session;
this.logger = logger;
this.translation = translation;
this.emailSender = emailSender;
}
IUser.cs:
public interface IUser<TKey>
{
TKey Id { get; set; }
string UserName { get; set; }
string Email { get; set; }
bool EmailConfirmed { get; set; }
}

You need to use the IdentityBuilder extension method AddSignInManager explicitly.
services.AddIdentity<Web.Security.Entities.User<int>, IdentityRole>()
.AddEntityFrameworkStores<SecurityDbContext>()
// Replace ApplicationUser with your concrete user class
.AddSignInManager<SignInManager<ApplicationUser>>()
.AddDefaultTokenProviders();
If you need access to it in your security service then you can inject it:
public class SecurityService<TUser, TKey> ...
{
public SecurityService(SignInManager<ApplicationUser> signInManager)
{
this.signInManager = signInManager;
}
}

Related

Error in ASP.NET Core : no service for type [...] has been registered

I get this error
No service for type 'Microsoft.AspNetCore.Identity.RoleManager`1[Microsoft.AspNetCore.Identity.IdentityRole]' has been registered
when I tried to create user and role in the startup file, can you please help me solve this issue. The error is happening on the line
var roleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();
Here's my full code:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddDbContext<LoginDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("LoginConnection")));
//ajouter le service d'authentification Identity
services.AddIdentity<LoginUser, LoginRole>(options =>
{
options.Password.RequiredLength = 6;
options.Password.RequiredUniqueChars = 2;
options.Password.RequireLowercase = false;
options.Password.RequireUppercase = false;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireDigit = false;
options.SignIn.RequireConfirmedEmail = false;
}).AddEntityFrameworkStores<LoginDbContext>()
.AddDefaultTokenProviders();
services.ConfigureApplicationCookie(options =>
{
options.LoginPath = "/Login/Login";
options.ReturnUrlParameter = "ReturnUrl";
options.LogoutPath = "/Login/Logout";
options.AccessDeniedPath = "/Login/AccessDenied";
options.ExpireTimeSpan = new TimeSpan(0, 15, 0);
});
//ajouter le service MVC
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider serviceProvider)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseAuthentication(); // ajouter le module d'authentification au middleware mvc
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
CreateRoles(serviceProvider);
}
private void CreateRoles(IServiceProvider serviceProvider)
{
var roleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();
var userManager = serviceProvider.GetRequiredService<UserManager<LoginUser>>();
Task<IdentityResult> roleResult;
string email = "someone#somewhere.com";
//Check that there is an Administrator role and create if not
Task<bool> hasAdminRole = roleManager.RoleExistsAsync("Administrator");
hasAdminRole.Wait();
if (!hasAdminRole.Result)
{
roleResult = roleManager.CreateAsync(new IdentityRole("Administrator"));
roleResult.Wait();
}
//Check if the admin user exists and create it if not
//Add to the Administrator role
Task<LoginUser> testUser = userManager.FindByEmailAsync(email);
testUser.Wait();
if (testUser.Result == null)
{
LoginUser administrator = new LoginUser();
administrator.Email = email;
administrator.UserName = email;
Task<IdentityResult> newUser = userManager.CreateAsync(administrator, "_AStrongP#ssword!");
newUser.Wait();
if (newUser.Result.Succeeded)
{
Task<IdentityResult> newUserRole = userManager.AddToRoleAsync(administrator, "Administrator");
newUserRole.Wait();
}
}
}
}
Error pîcture
var roleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();
The RoleManager type is IdentityRole, while you register it as LoginRole. Change it to this:
services.AddIdentity<LoginUser, IdentityRole>
In addition, the Authentication middleware should be before the Authorization middleware.
app.UseAuthentication();
app.UseAuthorization();

AllowAnonymous is not ignored in ASP.NET Core custom AuthenticationHandler after migrating from .NET Core 2.2 to 3.1

I have an ASP.NET Core 2.2 Web API which was working with the Basic Authentication. So far it worked fine and has no troubles. In one of the Controller, one Action Method is Decorated with [AllowAnonymous] to make the User Login, as usual.
[Produces("application/json")]
[Route("user")]
[AllowAnonymous]
[ApiController]
public class LoginController : ControllerBase
{
private readonly IConfiguration _configuration;
private readonly IMessagingService _messageService;
private readonly IBasicAuthenticationService _basicAuthenticationService;
private readonly string PWAPIBaseUrl;
public LoginController(IConfiguration configuration, ILogger<LoginController> logger, IMessagingService messagingService, IBasicAuthenticationService authenticationService)
{
_configuration = configuration;
_logger = logger;
_messageService = messagingService;
_basicAuthenticationService = authenticationService;
}
[HttpGet]
[AllowAnonymous]
[Route("login/{username}/{clientID}")]
public async Task<IActionResult> UserLogin(string username, string clientID)
{
// Check the Credentials Manually
string failReason = "";
if (!CheckCredentials(out failReason))
{
return StatusCode(StatusCodes.Status403Forbidden, userInfo);
}
// Load the Roles and UI Preferences ...
}
}
As the end of .NET Core 2.2 is near, I have tried upgrading to the .NET Core 3.1 and followed the official Migration Guide and made necessary changes. Though the Application started out smoothly, there is one bugging issue which forbids the upgrade.
On the above controller, the [AllowAnonymous] is not ignored and the Authentication is evaluated and thrown out with an error. But the Login method is executed after. This causes Login to break in all the dependent applications. I have tried all the suggestions from Stackoverflow like this, this and this.
Basic Authentication Handler:
public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
private readonly ILogger<BasicAuthenticationHandler> _logger = null;
private readonly IBasicAuthenticationService _basicAuthenticationService;
public BasicAuthenticationHandler(
IOptionsMonitor<AuthenticationSchemeOptions> options,
UrlEncoder encoder,
ILoggerFactory loggerFactory,
ISystemClock clock,
IBasicAuthenticationService authenticationService)
: base(options, loggerFactory, encoder, clock)
{
_logger = loggerFactory.CreateLogger<BasicAuthenticationHandler>();
_basicAuthenticationService = authenticationService;
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
var config = Util.GetConfig();
if (!Request.Headers.ContainsKey("Authorization"))
{
_logger.LogError("No authorization credentials");
return AuthenticateResult.NoResult();
}
if (!Request.Headers.ContainsKey("ClientID"))
{
_logger.LogError("Missing header client token");
return AuthenticateResult.Fail("Missing header client token");
}
var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
if (authHeader.Scheme != "Basic")
{
_logger.LogError("Authentication scheme not recognized");
return AuthenticateResult.Fail("Authentication scheme not recognized");
}
var credentialBytes = Convert.FromBase64String(authHeader.Parameter);
var credentials = Encoding.UTF8.GetString(credentialBytes).Split(':');
var username = credentials[0];
var password = credentials[1];
string fullname = "";
string failReason = "";
bool t = false;
IPrincipal principal = null;
// Do Business Validation against the DB
if (!t) // login failed
{
byte[] bEncodedResponse = Encoding.UTF8.GetBytes(failReason);
await Context.Response.Body.WriteAsync(bEncodedResponse, 0, bEncodedResponse.Length);
return AuthenticateResult.Fail(failReason);
}
else
{
var claims = new[]
{
new Claim(ClaimTypes.NameIdentifier, username),
new Claim(ClaimTypes.Name, fullname),
};
var identity = new ClaimsIdentity(claims, Scheme.Name);
principal = principal==null?new ClaimsPrincipal(identity): principal;
var ticket = new AuthenticationTicket(principal as ClaimsPrincipal, Scheme.Name);
return AuthenticateResult.Success(ticket);
}
}
}
Startup.cs
public class Startup
{
public Startup(IWebHostEnvironment environment, IConfiguration configuration, ILoggerFactory loggerFactory)
{
Environment = environment;
Configuration = configuration;
LoggerFactory = loggerFactory;
}
public IConfiguration Configuration { get; }
public ILoggerFactory LoggerFactory { get; }
public IWebHostEnvironment Environment { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication("BasicAuthentication")
.AddScheme<AuthenticationSchemeOptions, BasicAuthenticationHandler>("BasicAuthentication", null);
// Adding the Configuration Options -- Extension Methods to Inject Configuration as IOption POCOs
services.ConfigureAPIOptions(Configuration);
// configure DI for application services -- Other DI Objects
services.ConfigureDependencies(Configuration, LoggerFactory);
Common.APIConfiguration.Current = Configuration;
services.AddControllers();
services.AddAuthorization();
if (Environment.IsDevelopment())
{
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo { Title = "My Materials API", Version = "v1" });
});
}
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRouting();
if (env.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My Materials API v1");
c.RoutePrefix = string.Empty;
});
}
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
I am still clueless on what I did wrong and I might be missing something in ASP.NET Core 3.1. Please help me in getting this working. Thanks in advance.
EDIT 1:
ServiceExtensions.cs
public static class ServiceExtensions
{
public static void ConfigureAPIOptions(this IServiceCollection services, IConfiguration configuration)
{
services.AddOptions();
services.Configure<DataSetting>(configuration.GetSection("DataSettings"));
services.Configure<UrlSetting>(configuration.GetSection("UrlSettings"));
services.Configure<SiteSettings>(configuration.GetSection("SiteSettings"));
}
public static void ConfigureDependencies(this IServiceCollection services, IConfiguration configuration, ILoggerFactory loggerFactory)
{
services.AddSingleton<IConfiguration>(configuration);
services.AddScoped<IBasicAuthenticationService, BasicAuthenticationService>();
services.AddScoped<IMessagingService>(s => new MessagingServices(configuration, loggerFactory.CreateLogger<MessagingServices>()));
services.AddHostedService<TimedHostedService>();
}
}
A Small Kludge for accessing the Configuration, where DI is not possible.
public static class APIConfiguration
{
public static IConfiguration Current { get; set; }
}
I tried this and it really helps me.
private static bool HasAllowAnonymous(AuthorizationFilterContext context)
{
var filters = context.Filters;
for (var i = 0; i < filters.Count; i++)
{
if (filters[i] is IAllowAnonymousFilter)
{
return true;
}
}
// When doing endpoint routing, MVC does not add AllowAnonymousFilters for AllowAnonymousAttributes that
// were discovered on controllers and actions. To maintain compat with 2.x,
// we'll check for the presence of IAllowAnonymous in endpoint metadata.
var endpoint = context.HttpContext.GetEndpoint();
if (endpoint?.Metadata?.GetMetadata<IAllowAnonymous>() != null)
{
return true;
}
return false;
}
https://github.com/dotnet/aspnetcore/blob/bd65275148abc9b07a3b59797a88d485341152bf/src/Mvc/Mvc.Core/src/Authorization/AuthorizeFilter.cs#L236
It was mentioned here https://learn.microsoft.com/en-us/dotnet/core/compatibility/2.2-3.1#authorization-iallowanonymous-removed-from-authorizationfiltercontextfilters

What is the way in ASP.NET Core 3.0 to get current authenticated User username?

I'm working on the latest ASP.NET Core 3.0 Angular template. What I need to know is how we can get authenticated user, username in ActionFilterAttribute.
The ClaimTypes.NameIdentifier returns the current user id, and ClaimTypes.Name returns the username which is null.
Here is my code:
where getUser has a UserId of d15f997a-6f65-4eb2-aecb-8b525361ae50
I've also register IHttpContextAccessor in the Startup class as follows:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<ApplicationUser>()
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
services.AddAuthentication()
.AddIdentityServerJwt();
//Database Setup
services.AddDbContext<HashooDBContext>(Configuration.GetConnectionString("DefaultConnection"));
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddControllersWithViews();
services.AddRazorPages();
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
return ContainerSetup.InitializeWeb(Assembly.GetExecutingAssembly(), services);
}
Controller.cs:
[Authorize]
[ApiController]
[Route("api/[controller]/{companycode}")]
public class DashboardTransactionsController : ControllerBase
{
[CustomAuthorizationFilter("companycode")]
public string Get()
{
return "Welcome";
}
}
Please let me know what am I doing wrong?
You can use IHttpContextAccessor
private readonly IHttpContextAccessor _httpContextAccessor;
public UserService(
IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
And to get user name you can use
_httpContextAccessor?.HttpContext?.User?.Identity?.Name;
Or
_httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
Then register in your Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
For this issue, it is caused by that the IdentityServer4 did not add the JwtClaimTypes.Name to the access_token. If you check the access_token from web brower, you will find it miss "name": "Tom", node.
For a workaround, you could implement your own ITokenService like
public class CustomTokenService : DefaultTokenService
{
public CustomTokenService(IClaimsService claimsProvider
, IReferenceTokenStore referenceTokenStore
, ITokenCreationService creationService
, IHttpContextAccessor contextAccessor
, ISystemClock clock
, IKeyMaterialService keyMaterialService
, ILogger<DefaultTokenService> logger)
: base(claimsProvider, referenceTokenStore, creationService, contextAccessor, clock, keyMaterialService, logger)
{
}
public override async Task<Token> CreateAccessTokenAsync(TokenCreationRequest request)
{
Logger.LogTrace("Creating access token");
request.Validate();
var claims = new List<Claim>();
claims.AddRange(await ClaimsProvider.GetAccessTokenClaimsAsync(
request.Subject,
request.Resources,
request.ValidatedRequest));
if (request.ValidatedRequest.Client.IncludeJwtId)
{
claims.Add(new Claim(JwtClaimTypes.JwtId, CryptoRandom.CreateUniqueId(16)));
}
claims.Add(new Claim(JwtClaimTypes.Name, request.Subject.GetDisplayName()));
var issuer = Context.HttpContext.GetIdentityServerIssuerUri();
var token = new Token(OidcConstants.TokenTypes.AccessToken)
{
CreationTime = Clock.UtcNow.UtcDateTime,
Issuer = issuer,
Lifetime = request.ValidatedRequest.AccessTokenLifetime,
Claims = claims.Distinct(new ClaimComparer()).ToList(),
ClientId = request.ValidatedRequest.Client.ClientId,
AccessTokenType = request.ValidatedRequest.AccessTokenType
};
foreach (var api in request.Resources.ApiResources)
{
if (!string.IsNullOrWhiteSpace(api.Name))
{
token.Audiences.Add(api.Name);
}
}
return token;
}
}
And then register CustomTokenService before Identity Configuration
public void ConfigureServices(IServiceCollection services)
{
services.TryAddTransient<ITokenService, CustomTokenService>();
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<ApplicationUser>()
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
services.AddAuthentication()
.AddIdentityServerJwt();
}
For a simple way, you could try to override DefaultClaimsService.GetStandardSubjectClaims like
public class CustomClaimsService : DefaultClaimsService
{
public CustomClaimsService(IProfileService profile
, ILogger<DefaultClaimsService> logger) : base(profile, logger)
{
}
protected override IEnumerable<Claim> GetStandardSubjectClaims(ClaimsPrincipal subject)
{
var claims = base.GetStandardSubjectClaims(subject);
var newClaims = new List<Claim>(claims)
{
new Claim(JwtClaimTypes.Name, subject.Identity.Name)
};
return newClaims;
}
}
And register like
public void ConfigureServices(IServiceCollection services)
{
services.TryAddTransient<IClaimsService, CustomClaimsService>();
//services.TryAddTransient<ITokenService, CustomTokenService>();
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<ApplicationUser>()
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
services.AddAuthentication()
.AddIdentityServerJwt();
}
Try this code:
string userId = context.HttpContext.User.Claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier).Value;

Asp.net Core 2.2, custom middleware for authorizing files outside of wwwroot, but httpcontext.User is null

I need to allow access to a directory of static files only if the user is authenticated.
I have set up a middleware to check each request as it comes. I can get the request no problem, but the Httpcontext User is always null. I am using cookies authentication and not Identity. I have added the default authentication cookies scheme to the sign in as well as the services in startup.cs. I'm not sure why I can't get the user. This is based off of Scott Allen's tutorial https://odetocode.com/blogs/scott/archive/2015/10/06/authorization-policies-and-middleware-in-asp-net-5.aspx
Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<IdentityUser>()
.AddDefaultUI(UIFramework.Bootstrap4)
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddAuthentication( options =>
{
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignOutScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie(options =>
{
options.AccessDeniedPath = "/Home/Index";
options.LoginPath = "/Identity/Account/Login";
});
services.AddAuthorization(options =>
{
options.AddPolicy("Authenticated", policy => policy.RequireAuthenticatedUser());
});
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/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.UseProtectFolder(new ProtectFolderOptions
{
Path = "/StaticFiles",
PolicyName = "Authenticated",
});
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(Directory.GetCurrentDirectory(), "Static_Files")),
RequestPath = "/StaticFiles"
});
app.UseCookiePolicy();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
private object RedirectResult()
{
throw new NotImplementedException();
}
}
MiddleWare
public class ProtectFolderOptions
{
public PathString Path { get; set; }
public string PolicyName { get; set; }
}
// Extension method used to add the middleware to the HTTP request pipeline.
public static class ProtectFolderExtensions
{
public static IApplicationBuilder UseProtectFolder(this IApplicationBuilder builder, ProtectFolderOptions options)
{
return builder.UseMiddleware<ProtectFolder>(options);
}
}
// You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project
public class ProtectFolder
{
private readonly RequestDelegate _next;
private readonly PathString _path;
private readonly string _policyName;
public ProtectFolder(RequestDelegate next,ProtectFolderOptions options)
{
_next = next;
_path = options.Path;
_policyName = options.PolicyName;
}
public async Task Invoke(HttpContext httpContext, IAuthorizationService authorizationService)
{
if (httpContext.Request.Path.StartsWithSegments(_path))
{
var authorized = await authorizationService.AuthorizeAsync(
httpContext.User, null, _policyName);
if (authorized.Succeeded == false)
{
await httpContext.ChallengeAsync(CookieAuthenticationDefaults.AuthenticationScheme);
return;
}
}
await _next(httpContext);
}
}
The middleware is able to check the request. But httpcontext doesn't contain user even after I signed in, and I always get redirected to the login page.

.NET Core 2 ObjectDisposed exception on creating multiple users at once

I want to create 5 users at startup and use only these accounts. Standart registration mechanism is disabled.
So I found this example of code, it works but only for creating first user in this cycle. When it tries to create 4 other users, ObjectDisposed Exception is throwing.
I found almost the same issue on github, which seems to be still open. But I'm hope there can be some workaround about that.
//DbInitialize.cs
public async Task Initialize()
{
_context.Database.EnsureCreated();
if (!_context.Roles.Any(r => r.Name == "Administrator"))
{
await _roleManager.CreateAsync(new IdentityRole("Administrator"));
}
if (!_context.Users.Any())
{
for (int i = 0; i < 5; i++)
{
string userName = "testuser" + (i + 1);
string password = "Abc123456789";
//line where exception occures when i=1
var result =
await _userManager.CreateAsync(
new ApplicationUser {UserName = userName, Email = userName, EmailConfirmed = true},
password);
if (result.Succeeded)
{
var user = await _userManager.FindByEmailAsync(userName);
await _userManager.AddToRoleAsync(user, "Administrator");
}
}
}
}
//startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddMvc().AddViewOptions(options => options.HtmlHelperOptions.ClientValidationEnabled = false);
// Add Database Initializer
services.AddScoped<IDbInitializer, DbInitializer>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IDbInitializer dbInitializer)
{
if (env.IsDevelopment())
{
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseBrowserLink();
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
dbInitializer.Initialize();
}
Note: Initialization of _userManager is inside of DbInitializer class
private readonly ApplicationDbContext _context;
private readonly UserManager<ApplicationUser> _userManager;
private readonly RoleManager<IdentityRole> _roleManager;
public DbInitializer(
ApplicationDbContext context,
UserManager<ApplicationUser> userManager,
RoleManager<IdentityRole> roleManager)
{
_context = context;
_userManager = userManager;
_roleManager = roleManager;
}

Categories