This question already has answers here:
How to access DbContext in .NET 6 minimal API Program.cs
(1 answer)
how can i use dbInitializer.Initialize() in .net 6.0;
(1 answer)
Closed 3 months ago.
I'm coding a website using ASP.NET Core 6 from the course. The course was released in winter 2022 with old middleware. So I am facing problems with it. Here I have a DbInitializer file that contains user initialization data.
System.InvalidOperationException: 'Cannot resolve scoped service 'Mango.Services.Identity.Initializer.IDbInitializer' from root provider.'
DbInitializer.cs:
public class DbInitializer : IDbInitializer
{
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;
}
public void Initialize()
{
if (_roleManager.FindByNameAsync(SD.Admin).Result == null)
{
_roleManager.CreateAsync(new IdentityRole(SD.Admin)).GetAwaiter().GetResult();
_roleManager.CreateAsync(new IdentityRole(SD.Customer)).GetAwaiter().GetResult();
}
else
{
return;
}
ApplicationUser adminUser = new ApplicationUser()
{
UserName = "admin#gmail.com",
Email = "admin#gmail.com",
EmailConfirmed = true,
PhoneNumber = "111111111",
FirstName = "Ben",
LastName = "Admin"
};
_userManager.CreateAsync(adminUser,"Admin123").GetAwaiter().GetResult();
_userManager.AddToRoleAsync(adminUser, SD.Admin).GetAwaiter().GetResult();
var temp1 =_userManager.AddClaimsAsync(adminUser, new Claim[]{
new Claim(JwtClaimTypes.Name, adminUser.FirstName+" "+ adminUser.LastName),
new Claim(JwtClaimTypes.GivenName, adminUser.FirstName),
new Claim(JwtClaimTypes.FamilyName, adminUser.LastName),
new Claim(JwtClaimTypes.Role, SD.Admin)
}).Result;
ApplicationUser customerUser = new ApplicationUser()
{
UserName = "customer1#gmail.com",
Email = "customer1#gmail.com",
EmailConfirmed = true,
PhoneNumber = "111111111",
FirstName = "Ben",
LastName = "Cust"
};
_userManager.CreateAsync(customerUser, "Admin123").GetAwaiter().GetResult();
_userManager.AddToRoleAsync(customerUser, SD.Customer).GetAwaiter().GetResult();
var temp2 = _userManager.AddClaimsAsync(customerUser, new Claim[]{
new Claim(JwtClaimTypes.Name, customerUser.FirstName+" "+ customerUser.LastName),
new Claim(JwtClaimTypes.GivenName, customerUser.FirstName),
new Claim(JwtClaimTypes.FamilyName, customerUser.LastName),
new Claim(JwtClaimTypes.Role, SD.Customer)
}).Result;
}
}
Then my Program.cs file:
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllersWithViews();
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>().AddDefaultTokenProviders();
var identityBuilder = builder.Services.AddIdentityServer(options =>
{
options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
options.EmitStaticAudienceClaim = true;
}).AddInMemoryIdentityResources(SD.IdentityResourses)
.AddInMemoryApiScopes(SD.ApiScopes)
.AddInMemoryClients(SD.Clients)
.AddAspNetIdentity<ApplicationUser>();
builder.Services.AddScoped<IDbInitializer, DbInitializer>();
identityBuilder.AddDeveloperSigningCredential();
builder.Services.AddControllersWithViews();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
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.UseStaticFiles();
app.UseRouting();
app.UseIdentityServer();
app.UseAuthorization();
app.Services.GetRequiredService<IDbInitializer>().Initialize();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
I myself have not so long started to dive into the world of asp.net core coding and therefore encounter such problems. Can you tell me how to solve this problem? All the ways I found did not help.
Related
I am basically following the instruction on this youtube video https://www.youtube.com/watch?v=s2zJ_g-iQvg&ab_channel=CodAffection my problem is that I am using ASP.NET CORE while he is using ASP.NET CORE 2.2. And for some reason my api calls allways return 401 despite me having change only some line of code to make it compatible with ASP.NET CORE 2.2.
I want to make API calls when the user is login I am using ASP.NET CORE 3.1. I have made a login function which works. When I make an API call with postman the login function returns a token. However when I call another route (clients) and provided the bearer token I get a 401 error. I have searched for days but am unable to solve the problem. I have tried many different tutorials but I keep having the same problem. Thank you for your help.
[HttpPost, Route("login")]
public async Task<IActionResult> Login([FromBody] LoginModel user)
{
var userFromDb = await _userManager.FindByNameAsync(user.UserName);
if (user != null && true)//await _userManager.CheckPasswordAsync(user, user.Password))
{
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[] {
new Claim("UserID", "1")
}),
Expires = DateTime.UtcNow.AddHours(1),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes("superlongKeyWithALotOfWordsToMakeItMoreSecureWhichIsGoodThankYouForReadingMySecretKey#45")), SecurityAlgorithms.HmacSha256Signature)
};
var tokenHandler = new JwtSecurityTokenHandler();
var securityToken = tokenHandler.CreateToken(tokenDescriptor);
var token = tokenHandler.WriteToken(securityToken);
return Ok(new { token });
}
else
{
return Unauthorized();
}
}
Here is what my startup file looks like
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<ApplicationSettings>(Configuration.GetSection("AppSettings"));
services.AddMvc();
services.AddTransient<DatabaseMigrator>();
services.AddDbContext<erp_colombiaDbContext>(options => options.UseMySql(
Configuration.GetConnectionString("DefaultConnection"),
optionsBuilder => optionsBuilder.MigrationsAssembly(typeof(DesignTimeDbContextFactory).Assembly.FullName)));
services.TryAddScoped<UserManager<Employee>>();
services.TryAddScoped<SignInManager<Employee>>();
services.AddIdentityCore<Employee>(options =>
options.SignIn.RequireConfirmedAccount = false
).AddEntityFrameworkStores<erp_colombiaDbContext>();
//Jwt Authentication
var key = Encoding.UTF8.GetBytes("superlongKeyWithALotOfWordsToMakeItMoreSecureWhichIsGoodThankYouForReadingMySecretKey#45");//Configuration["AppSettings:Secret"].ToString());
services.Configure<IdentityOptions>(options =>
{
options.Password.RequireDigit = false;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireLowercase = false;
options.Password.RequireUppercase = false;
options.Password.RequiredLength = 4;
});
services.AddCors();
//Jwt Authentication
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(x =>
{
x.RequireHttpsMetadata = false;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false,
ClockSkew = TimeSpan.Zero
};
});
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
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, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
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.UseStaticFiles();
if (!env.IsDevelopment())
{
app.UseSpaStaticFiles();
}
app.UseRouting();
// global cors policy
app.UseCors(x => x
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
});
app.UseSpa(spa =>
{
// To learn more about options for serving an Angular SPA from ASP.NET Core,
// see https://go.microsoft.com/fwlink/?linkid=864501
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
});
}
}
and here is part of my appSettings.json setings.
"AppSettings": {
"Secret": "superlongKeyWithALotOfWordsToMakeItMoreSecureWhichIsGoodThankYouForReadingMySecretKey#45",
"ClientURL": "https://localhost:44344"
},
Here is the other route (clients)
// GET api/clients
[HttpGet]
[Authorize]
public IEnumerable<ClientViewModel> Get()
{
ClientViewModel clientViewModel;
List<ClientViewModel> listClientViewModels = new List<ClientViewModel>();
var clients = Task.Run(async () => await _clientService.GetAllClients()).Result;
foreach (var client in clients)
{
clientViewModel = new ClientViewModel();
clientViewModel.ClientId = client.ClientId;
clientViewModel.Active = client.Active;
clientViewModel.Address = client.Address;
clientViewModel.City = client.City;
clientViewModel.ClienteName = client.ClienteName;
clientViewModel.ComercialEmployeeId = client.ComercialEmployeeId;
clientViewModel.Confirmed = client.Confirmed;
clientViewModel.CountryId = client.CountryId;
clientViewModel.CreationDate = client.CreationDate;
clientViewModel.DANE = client.DANE;
clientViewModel.Department = client.Department;
clientViewModel.ElectronicBillingEmail = client.ElectronicBillingEmail;
clientViewModel.Eliminated = client.Eliminated;
clientViewModel.NIT = client.NIT;
clientViewModel.PostalCode = client.PostalCode;
clientViewModel.Phone = client.Phone;
listClientViewModels.Add(clientViewModel);
}
return listClientViewModels;
}
Here is how I add the token in postman.
fix your startup, add before app.UseEndpoints
....
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
.....
Add the options.Events to your AddJwtBearer:
.AddJwtBearer(options =>
{
options.Events = new JwtBearerEvents
{
OnAuthenticationFailed = c =>
{
// break point here
return Task.CompletedTask;
},
};
});
Then you'll get some context.exception:
(right-click image, open in new window/tab)
You should never do this on asp.net
// Task.Run is a no no
Task.Run(async () => await _clientService.GetAllClients()).Result;
You're using locking up 2 threads at the same time for no benefit.
Instead:
// GET api/clients
[HttpGet]
[Authorize]
public Task<IEnumerable<ClientViewModel>> Get()
and
var clients = await _clientService.GetAllClients();
For .Net Core you need to use a authorization provider like Identity Server:
https://identityserver4.readthedocs.io/en/latest/
I figure it out I had this code that overwrite the [Authorize] attribute.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AuthorizeAttribute : Attribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
var user = (User)context.HttpContext.Items["User"];
if (user == null)
{
// not logged in
context.Result = new JsonResult(new { message = "Unauthorized" }) { StatusCode = StatusCodes.Status401Unauthorized };
}
}
}
I removed it and now it work I dont understand why the code was there. Sorry, for my question.
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();
I'm trying to create some roles for my web application but it isn't really working because of a Tkey exception.
I'm happy if you give an upvote so the other guys who need help can see it probably more.
I don't know how I can fix it. I think there is a problem with my Startup.cs.
Whatever i try to add the DefaultIdentity and adding the roles.
Startup.cs - On this line I get an Error:
services.AddDefaultIdentity<IdentityRole>().AddRoles<IdentityRole>().AddDefaultUI().AddEntityFrameworkStores<VerwaltungsprogrammContext>();
This is the Error Message:
>AddEntityFrameworkStores can only be called with a user that derives from IdentityUser
namespace Verwaltungsprogramm
{
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.AddSession();
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<VerwaltungsprogrammContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("VerwaltungsprogrammContext")));
//services.AddDefaultIdentity<IdentityUser>();
--------------> services.AddDefaultIdentity<IdentityRole>().AddRoles<IdentityRole>().AddDefaultUI().AddEntityFrameworkStores<VerwaltungsprogrammContext>(); <--------------
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
.AddRazorPagesOptions(options =>
{
options.AllowAreas = true;
options.Conventions.AuthorizeAreaFolder("Logins", "/Create");
options.Conventions.AuthorizeAreaPage("Logins", "/Logout");
});
services.ConfigureApplicationCookie(options =>
{
options.LoginPath = $"/Logins/Index";
options.LogoutPath = $"/Logins/Logout";
options.AccessDeniedPath = $"/Cars/Index";
});
//Password Strength Setting
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.AllowedUserNameCharacters =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._#+";
options.User.RequireUniqueEmail = false;
});
//Seting the Account Login page
services.ConfigureApplicationCookie(options =>
{
// Cookie settings
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(5);
options.LoginPath = "/Logins/Create"; // If the LoginPath is not set here, ASP.NET Core
will default to /Account/Login
options.AccessDeniedPath = "/Cars/Index"; // If the AccessDeniedPath is not set here,
ASP.NET Core will default to /Account/AccessDenied
options.SlidingExpiration = true;
});
services.AddSingleton<IEmailSender, EmailSender>();
}
public class EmailSender : IEmailSender
{
public Task SendEmailAsync(string email, string subject, string message)
{
return Task.CompletedTask;
}
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider serviceProvider)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseSession();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
Seed.CreateRoles(serviceProvider, Configuration).Wait();
}
}
}
Error:
AddEntityFrameworkStores can only be called with a user that derives from IdentityUser
The Seed.cs file is to create some roles
Here is my Seed.cs
namespace Verwaltungsprogramm
{
public static class Seed
{
public static async Task CreateRoles(IServiceProvider serviceProvider, IConfiguration Configuration)
{
//adding customs roles
var RoleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();
var UserManager = serviceProvider.GetRequiredService<UserManager<ApplicationUser>>();
string[] roleNames = { "Admin", "Manager", "Member" };
IdentityResult roleResult;
foreach (var roleName in roleNames)
{
// creating the roles and seeding them to the database
var roleExist = await RoleManager.RoleExistsAsync(roleName);
if (!roleExist)
{
roleResult = await RoleManager.CreateAsync(new IdentityRole(roleName));
}
}
// creating a super user who could maintain the web app
var poweruser = new ApplicationUser
{
UserName = Configuration.GetSection("AppSettings")["UserEmail"],
Email = Configuration.GetSection("AppSettings")["UserEmail"]
};
string userPassword = Configuration.GetSection("AppSettings")["UserPassword"];
var user = await UserManager.FindByEmailAsync(Configuration.GetSection("AppSettings")["UserEmail"]);
if (user == null)
{
var createPowerUser = await UserManager.CreateAsync(poweruser, userPassword);
if (createPowerUser.Succeeded)
{
// here we assign the new user the "Admin" role
await UserManager.AddToRoleAsync(poweruser, "Admin");
}
}
}
}
}
Does the error also occur if you write the line in your Startup.cs like this?
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<VerwaltungsprogrammContext>();
I fixed it by creating the project again and switch to user Account authentication, for everyone who has the same problem, I recommend doing that.
It is my first time to use ASP.NET core 2.2 Identity to manage Authorization in Controller. In the client side, I use (Login.cshtml.cs) SignInManger.PasswordSignInAsync(…) to sign in a user. After user sign in successfully, I send HttpClient call to UsersController in another project to get more user’s info. However, the Controller is does not receive the the Claims.ClaimsPrincipal (User) information from the Request. The [Authorize(Roles = “admin”)] fails but [AllowAnonymous] passes. I checked the Request cookie. The User HttpContext is not updated to the signed in user in client side. To be exact, I don’t know how to update the User in cookie. I thought it is done in the background. I have looked through many posts but none of them explicitly mentions how the User is being updated in the cookie and send to the controller. It seems that the User updated is done after the program exits the Login.cshtml.cs. I am able to get the Identity of the user by using SingInManager.CreateUserPrincipalAsync(…) within public async Task OnPostAsync(string returnUrl = null) of Login.cshtml.cs. But I don’t know how to pass that Identity to the User in cookie and send it to Controller.
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
returnUrl = returnUrl ?? Url.Content("~/");
if (ModelState.IsValid)
{
var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
// test: get the Identity of the Input.Email
var user = await _signInManager.UserManager.FindByEmailAsync(Input.Email);
var userPrincipal = await _signInManager.CreateUserPrincipalAsync(user);
var identity = userPrincipal.Identity;
// * User Identity does not populate at this point.
var ret = await GetRestOfUserInfo();
}
}
return Page();
}
private async Task<int> GetRestOfUserInfo()
{
var userStatus = await _userManager.GetUserAsync(User);
UserCSDto userCDDto = new UserCSDto()
{
Email = Input.Email,
UserName = Input.Email,
Password = Input.Password,
RememberMe = Input.RememberMe
};
CookieContainer cookieContainer = new CookieContainer();
HttpClientHandler handler = new HttpClientHandler
{
UseCookies = true,
UseDefaultCredentials = true,
CookieContainer = cookieContainer
};
var client = new HttpClient(handler);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var content = new StringContent(JsonConvert.SerializeObject(userCDDto), Encoding.UTF8, "application/json");
var url = _connectionSettings.UrlConnection + "/api/users/authenticate";
var response = await client.PostAsync(url, content);
if (response.IsSuccessStatusCode)
{
var respUser = JsonConvert.DeserializeObject<User>(response.Content.ReadAsStringAsync().Result);
var _responseToken = response.Headers.Where(t => t.Key == "Authorization")
.Select(t => t.Value);
var token = (_responseToken.FirstOrDefault()).FirstOrDefault();
applicationUser.PreferredName = respUser.PreferredName;
applicationUser.Token = token;
ApplicationUser user = await _userManager.FindByEmailAsync(Input.Email);
user.Token = token;
user.PreferredName = respUser.PreferredName ?? "";
IEnumerable<Claim> claims = new Claim[]
{
new Claim("Token", token),
new Claim("PreferredName", "123abc") // respUser.PreferredName)
};
await _userManager.AddClaimsAsync(user, claims);
return 1;
}
return 0;
}
[Authorize]
[ApiController]
[Route("api/[controller]")]
[Produces("application/json")]
public class UsersController : ControllerBase
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private IUserService _userService;
public UsersController(UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager)
{
_userManager = userManager;
_signInManager = signInManager;
}
:
:
[Authorize(Roles = "any role")] // failed because User does not contain any info
[AllowAnonymous] // works because no authorization check
[HttpPost("authenticate")]
public async Task<IActionResult> Authenticate([FromBody]UserCSDto userParam)
{
var username = User.Identity.Name; // username is null because User does not
// have info.
var user = _userService.Authenticate(userParam.UserName, userParam.Password);
if (user == null)
return BadRequest(new { message = "Username or passwrod is incorrect" });
var token = _jwtTokenService.CreateToken(userCSDto);
Response.Headers.Add("Authorization", "Bearer " + token);
Response.Headers.Add("Content-Type", "application/json");
return Ok(userCSDto);
}
:
:
}
/////////////////////////////
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IConfiguration>(Configuration);
services.Configure<ConnectionSettings>(Configuration.GetSection("ConnectionStrings"));
var connection = Configuration.GetConnectionString("DefaultConnection");
services.AddDbContext<EFCoreContext>(
options => options.UseSqlServer(connection,
b => b.MigrationsAssembly("EFStructures"))); // from Data project
services.AddHttpContextAccessor(); // #.Net Core 2.2
services.ConfigureApplicationCookie(options =>
{
options.Cookie.HttpOnly = true;
options.Cookie.Expiration = TimeSpan.FromHours(1);
options.SlidingExpiration = true;
});
services.AddSession(options =>
{
options.Cookie.HttpOnly = true;
options.Cookie.Name = "Test.Session";
options.IdleTimeout = TimeSpan.FromMinutes(60);
options.Cookie.Path = "/";
});
services.Configure<IdentityOptions>(options =>
{
// Password settings.
options.Password.RequireDigit = true;
options.Password.RequireLowercase = true;
options.Password.RequireNonAlphanumeric = true;
options.Password.RequireUppercase = true;
options.Password.RequiredLength = 6;
options.Password.RequiredUniqueChars = 1;
// Lockout settings.
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
options.Lockout.MaxFailedAccessAttempts = 5;
options.Lockout.AllowedForNewUsers = true;
// User settings.
options.User.AllowedUserNameCharacters =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._#+";
options.User.RequireUniqueEmail = true;
// Require Confirmed and Unique Email
options.User.RequireUniqueEmail = true;
options.SignIn.RequireConfirmedEmail = false;
});
#region authenication using JWT token
services.Configure<JwtTokenSettings>(Configuration.GetSection("JwtTokenSettings"));
///// configure jwt authentication
var jwtTokenSettings = Configuration.GetSection("JwtTokenSettings").Get<JwtTokenSettings>();
var key = Encoding.ASCII.GetBytes(jwtTokenSettings.Secret);
//Adds cookie middleware to the services collection and configures it
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options => options.LoginPath = new PathString("/account/login"));
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false,
//ValidateIssuer = Configuration["AppSettings:Issuer"],
//ValidateAudience = Configuration["AppSettings:Issuer"],
ValidateLifetime = true
};
});
#endregion
#region Add Role services to Identity RAZOR PAGE only
services.AddIdentity<ApplicationUser, IdentityRole>()
// .AddDefaultUI(Microsoft.AspNetCore.Identity.UI.UIFramework.Bootstrap4)
.AddRoles<IdentityRole>()
.AddRoleManager<RoleManager<IdentityRole>>()
.AddDefaultTokenProviders()
.AddEntityFrameworkStores<EFCoreContext>();
#endregion
#region Setting for using Identity
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;
});
#endregion Setting for using Identity
services.AddHttpClient();
services.AddScoped<IJwtTokenService, JwtTokenService>();
services.AddScoped<IEmailSenderService, EmailSenderService>();
services.AddScoped<IEmailSender, EmailSender>();
#region Get EmailSettings from appsettings.json
services.AddOptions();
services.Configure<EmailSettings>(Configuration.GetSection("EmailSettings"));
#endregion Get EmailSettings from appsettings.json
services.AddMvc()
.ConfigureApiBehaviorOptions(options =>
{
options
.SuppressUseValidationProblemDetailsForInvalidModelStateResponses = true;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider serviceProvider, ILoggerFactory loggerFactory)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseAuthentication();
app.UseSession();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseRequestLocalization();
app.UseMvcWithDefaultRoute();
// global cors policy
app.UseCors(x => x
.WithOrigins("https://localhost")
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
app.UseMvc();
}
}
I am unable to use RoleManager in my application. I am trying to seed an Admin user in to a .NET Core web application, and I want this user to have a role called "canEdit". What I've got currently produces this error:
System.InvalidOperationException: 'No service for type 'Microsoft.AspNetCore.Identity.RoleManager`1[Microsoft.AspNetCore.Identity.IdentityRole]' has been registered.'
I have tried various ways of doing this. I have tried using Service Provider to do it, dependency injection, etc. but they all give the same error.
ServiceProvider in the Configure method of Startup.cs:
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseAuthentication();
var scopeFactory = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>();
var scope = scopeFactory.CreateScope();
var roleManager = scope.ServiceProvider.GetRequiredService<RoleManager<IdentityRole>>();
DbInitializer dbi = new DbInitializer(roleManager);
dbi.Initialize(context, userManager);
DBInitializer Class:
Constructor:
private readonly RoleManager<IdentityRole> rm;
public DbInitializer(RoleManager<IdentityRole> rm)
{
this.rm = rm;
}
CreateAdmin method:
private async Task CreateAdmin(UserManager<ApplicationUser> userManager, ApplicationDbContext context)
{
IdentityResult ir;
ir = await rm.CreateAsync(new IdentityRole("canEdit"));
IdentityRole canEdit = new IdentityRole("canEdit");
ApplicationUser admin = new ApplicationUser
{
UserName = "Member1#email.com"
};
if (context.Users.Where(u => u.UserName == admin.UserName).Count() == 0)
{
userManager.CreateAsync(admin, "Password123!").Wait();
userManager.AddToRoleAsync(admin, "canEdit").Wait();
}
}
CreateUsers method:
private void CreateUsers(UserManager<ApplicationUser> userManager, ApplicationDbContext context)
{
CreateAdmin(userManager, context).Wait();
ApplicationUser customer1 = new ApplicationUser
{
UserName = "Customer1#email.com"
};
if (context.Users.Where(u => u.UserName == customer1.UserName).Count() > 0)
{
userManager.CreateAsync(customer1, "Password123!").Wait();
}
This repeats for customer2, 3, 4, 5. I can optimise this by removing the need to create an object which I may not need (in the case where the email exists), but I was trying to knock up this method and the CreateAdmin method quickly, then optimise later. Unfortunately I then ran in to an error which I have been unable to fix.
The end goal is for the DbInitializer class to seed 5 regular users, and 1 admin user with extra permissions/claims.
Do you have the Identity services registered?
Also, I ran into issues when I used .Wait() instead of await.
services.AddIdentity<ApplicationUser,IdentityRole>(options=>
{
options.User.RequireUniqueEmail = true;
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
})
.AddDefaultTokenProviders()
.AddDefaultUI()
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddAuthorization();
services.AddAuthentication();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
If this helps, here is how I seeded my Db.
public SeedDataBase(IServiceProvider _serviceProvider, ApplicationDbContext _context)
{
serviceProvider = _serviceProvider;
context = _context;
}
private IServiceProvider serviceProvider;
private ApplicationDbContext context;
private ApplicationUser superUser;
public async Task Seed()
{
await CreateSuperUser();
await SeedDb();
}
private async Task CreateSuperUser()
{
var _userManager = serviceProvider.GetRequiredService<UserManager<ApplicationUser>>();
var userExists = await _userManager.GetUsersInRoleAsync("FULLADMIN");
if (userExists.Count() < 1)
{
var _roleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();
var _signinManger = serviceProvider.GetRequiredService<SignInManager<ApplicationUser>>();
superUser = new ApplicationUser()
{
UserName = "superuser#superuser.com",
Email = "superuser#superuser.com",
FirstName = "Super",
LastName = "User",
AccountCreationDate = DateTime.Now.ToShortDateString(),
Access = ApplicationUser.Permissions.FullAdmin
};
var permissions = Enum.GetNames(typeof(ApplicationUser.Permissions));
foreach (var s in permissions)
{
await _roleManager.CreateAsync(new IdentityRole(s));
}
await _userManager.CreateAsync(superUser, "SecureP#ssword1234");
await _userManager.AddToRoleAsync(superUser, Enum.GetName(typeof(ApplicationUser.Permissions), superUser.Access));
}
}
that may help someone in the future ,
you have to use the await for the RoleManager.CreateAsync method
then to verify if the operation was completed successfully
var res = await _roleManager.CreateAsync(new IdentityRole(Role));
if(res.Succeeded)
{
return Ok("created successfully !");
}
If you use IdentityServer4 or Duende.IdentityServer in .NET 5 < with Individual user accounts then edit Startup.cs. Look for the following values:
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
Edit it to look like this:
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();