Asp.Net core MVC6 How to initially add roles in Identity 3 - asp.net-mvc

I've looked for this in Stackoverflow and so far it appears there are plenty of questions on adding roles in Identity 1 & 2 but its different in Identity 3.
I want to seed roles in the database. I have just two. I intended to use _roleManager which I have injected into the class. Thats fine. My problem is.. there doesnt appear to be any method to actually add a role.. Using CreateAsync is for adding the user to the role.. how do you code to add a role using "_userManager" or do you have to do it another way?

EDIT
Identity
In Identity RoleManager is for creating roles and UserManager is for adding users to roles.
This is an example to point you in the right direction. The code below is for creating a new role Administrator
if (!roleManager.RoleExists("Administrator"))
{
MyIdentityRole newRole = new MyIdentityRole("Administrator", "Administrators can do something with data");
roleManager.Create(newRole);
}
EDIT
Further, this is for adding a user to a role and this also an example:
\\assuming you test if the user has been assigned to the role "Administrator" before adding them to that role
if(RoleAdministrator == true){
userManager.AddToRole(User.Id, "Administrator");
}

public class SeedData
{
private const string _adminRoleName = "admin";
private string _adminEmail = "admin#demo.com";
private string _adminPassword = "P#ssw0rd!PK";
private string[] _roles = new string[] { _adminRoleName, "supervisor" };
private readonly RoleManager<IdentityRole<Guid>> _roleManager;
private readonly UserManager<ApplicationUser> _userManager;
public static async Task Run(IServiceProvider serviceProvider)
{
using (var serviceScope =serviceProvider
.GetRequiredService<IServiceScopeFactory>()
.CreateScope())
{
var instance = serviceScope.ServiceProvider.GetService<SeedData>();
await instance.Initialize();
var context = serviceScope.ServiceProvider.GetService<AppDbContext>();
if (!context.Products.Any())
{
// Seed Other entities Here
}
await context.SaveChangesAsync();
}
}
public SeedData(UserManager<ApplicationUser> userManager,
RoleManager<IdentityRole<Guid>> roleManager)
{
_roleManager = roleManager;
_userManager = userManager;
}
public async Task Initialize()
{
foreach (var role in _roles)
{
if (!await _roleManager.RoleExistsAsync(role))
{
await _roleManager.CreateAsync(new IdentityRole<Guid>(role));
}
}
var adminUsers = await _userManager.GetUsersInRoleAsync(_adminRoleName);
if (!adminUsers.Any())
{
var adminUser = new ApplicationUser()
{
Id = Guid.NewGuid(),
Email = _adminEmail,
UserName = _adminEmail
};
var result = await _userManager.CreateAsync(adminUser, _adminPassword);
if(result.Success)
{
await _userManager.AddToRoleAsync(adminUser, _adminRoleName);
}
}
}
}
In your Program.cs
public static void Main(string[] args)
{
var host = BuildWebHost(args);
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
try
{
SeedData.Run(services).Wait();
}
catch (Exception ex)
{
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "Error while seeding database.");
}
}
host.Run();
}
Might be helpful to someone :)

Related

Why does this error occur: System.AggregateException

During seeding data, this error occurs:
These are the steps:
public static async Task SeedUsersAndRolesAsync(IApplicationBuilder applicationBuilder)
{
using (var serviceScope = applicationBuilder.ApplicationServices.CreateScope())
{
var roleManager = serviceScope.ServiceProvider.GetRequiredService<RoleManager<IdentityRole>>();
if (!await roleManager.RoleExistsAsync(UserRoles.Admin))
{
await roleManager.CreateAsync(new IdentityRole(UserRoles.Admin));
}
if (!await roleManager.RoleExistsAsync(UserRoles.User))
{
await roleManager.CreateAsync(new IdentityRole(UserRoles.User));
}
var userManager = serviceScope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();
var adminUser = await userManager.FindByEmailAsync(AdminInfo.FirstGmail);
if (adminUser is null)
{
var newAdminUser = new ApplicationUser()
{
FullName = AdminInfo.FirstFullName,
UserName = AdminInfo.FirstUsername,
Email = AdminInfo.FirstGmail,
EmailConfirmed = true
};
await userManager.CreateAsync(newAdminUser, AdminInfo.FirstPassword);
await userManager.AddToRoleAsync(newAdminUser, UserRoles.Admin);
}
var simpleUser = await userManager.FindByEmailAsync(AdminInfo.SecondGmail);
if (simpleUser is null)
{
var newSimpleUser = new ApplicationUser()
{
FullName = AdminInfo.SecondFullName,
UserName = AdminInfo.SecondUsername,
Email = AdminInfo.SecondGmail,
EmailConfirmed = true
};
await userManager.CreateAsync(newSimpleUser, AdminInfo.SecondPassword);
await userManager.AddToRoleAsync(newSimpleUser, UserRoles.User);
}
}
}
This is the custom ApplicationUser class:
public class ApplicationUser : IdentityUser
{
[Display(Name = "Full Name")]
public string FullName { get; set; }
}
And the program.cs looks like this:
builder.Services.AddIdentity<ApplicationUser, IdentityRole>().AddEntityFrameworkStores<AppDbContext>();
builder.Services.AddMemoryCache();
builder.Services.AddSession();
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
});
app.UseSession();
app.UseAuthentication();
app.UseAuthorization();
And finally used the seeding class in the program.cs:
AppDbInitializer.Seed(app);
AppDbInitializer.SeedUsersAndRolesAsync(app).Wait();
app.Run();
But that error happened when I tried to run the app.
After using break point in "SeedUsersAndRolesAsync" class, I understand the error is happening in this line:
await userManager.AddToRoleAsync(newAdminUser, UserRoles.Admin);
And this is the inner exception:
- InnerException {"The INSERT statement conflicted with the FOREIGN KEY constraint \"FK_AspNetUserRoles_AspNetUsers_UserId\". The conflict occurred in database \"MovieTicketDB\", table \"dbo.AspNetUsers\", column 'Id'.\r\nThe statement has been terminated."} System.Exception {Microsoft.Data.SqlClient.SqlException}
Please help me to find my mistake, thanks in advance.
You are trying to add some Users for a Role that still doesn't exist in the database... Try to save changes before adding the Role for the User.

How to seed the Authentication db in ASP NET MVC

In which module do I do the seeding of the db? I want to add roles and users if they do not exist yet.
In NET Core I would use the startup file in this way:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
using (IServiceScope serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
var dbContext = serviceScope.ServiceProvider.GetService<ApplicationDbContext>();
var roleManager = serviceScope.ServiceProvider.GetService<RoleManager<IdentityRole>>();
var userManager = serviceScope.ServiceProvider.GetService<UserManager<User>>();
DbSeeder.Seed(Configuration, dbContext, roleManager, userManager);
}
}
and in my class DBSeeder:
public static class DbSeeder
{
public static void Seed(IConfiguration configuration,
ApplicationDbContext dbContext,
RoleManager<IdentityRole> roleManager,
UserManager<User> userManager)
{
if (!dbContext.Users.Any()) CreateUsers(configuration, dbContext, roleManager, userManager).GetAwaiter().GetResult();
}
private static async Task CreateUsers(IConfiguration configuration,
ApplicationDbContext dbContext,
RoleManager<IdentityRole> roleManager,
UserManager<User> userManager)
{
string role_Administrator = "Administrator";
string role_RegisteredUser = "RegisteredUser";
if (!await roleManager.RoleExistsAsync(role_Administrator))
{
await roleManager.CreateAsync(new IdentityRole(role_Administrator));
}
if (!await roleManager.RoleExistsAsync(role_RegisteredUser))
{
await roleManager.CreateAsync(new IdentityRole(role_RegisteredUser));
}
var user_Admin = new User()
{
SecurityStamp = Guid.NewGuid().ToString(),
UserName = "Admin",
Email = configuration["ContactUs"],
DisplayName = "Admin",
EmailConfirmed = true
};
if (await userManager.FindByNameAsync(user_Admin.UserName) == null)
{
await userManager.CreateAsync(user_Admin, "Pass4Admin");
await userManager.AddToRoleAsync(user_Admin, role_RegisteredUser);
await userManager.AddToRoleAsync(user_Admin, role_Administrator);
}
await dbContext.SaveChangesAsync();
}

I am trying to seed users and user roles in asp.net but only the roles succeed

Hey This is probably a newbie question but I honestly can't find the problem after hours of searching for answers.
I am trying to create two roles and an admin user by seeding the database. I get no compilation errors but after running the application I couldn't login so I checked the database and noticed that the roles were created successfully but not the admin user.
Here is the class that I use for seeding:
public class Initialize
{
public static async Task InitializeAsync(ApplicationDbContext context, IServiceProvider serviceProvider)
{
var RoleManager = serviceProvider.GetRequiredService < RoleManager<IdentityRole>>();
UserManager<ApplicationUser> userManager = serviceProvider.GetRequiredService<UserManager<ApplicationUser>>();
string[] rolenames = { "Admin","Member"};
IdentityResult roleResult;
foreach (var roleName in rolenames) {
var roleExist = await RoleManager.RoleExistsAsync(roleName);
if (!roleExist)
{
roleResult = await RoleManager.CreateAsync(new IdentityRole(roleName));
}
}
string username = "admin";
string email = "admin#example.com";
string password = "Secret123";
string role = "Admin";
if (await userManager.FindByEmailAsync(username) == null)
{
ApplicationUser user = new ApplicationUser
{
UserName = username,
Email = email
};
IdentityResult result = await userManager
.CreateAsync(user, password);
if (result.Succeeded)
{
await userManager.AddToRoleAsync(user, role);
}
}
}
}
My ConfigureServices :
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
// Add application services.
services.AddTransient<IEmailSender, EmailSender>();
services.AddMvc();
}
And Here is my Program.cs :
public class Program
{
public static void Main(string[] args)
{
var host = BuildWebHost(args);
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
try
{
var context = services.GetRequiredService<ApplicationDbContext>();
Initialize.InitializeAsync(context, services).Wait();
}
catch(Exception ex)
{
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "Error seeding");
}
}
host.Run();
}
Any help is appreciated :)
Try look at userManager -> Store -> Users -> Results Views -> Message after access via serviceProvider.GetRequiredService. You can have some error message about it here.
If it contains a message:
"Cannot create a DbSet for 'ApplicationUser' because this type is not
included in the model for the context."
you must changed ApplicationContext class inherit from IdentityDbContext and it should be works fine.

Invalid Username or password on Seeded User Account MVC5

I am working in a new MVC5 application that I built specifically to test this.
I have two db contexts. The configuration.cs for the ApplicationDbContext is as follows:
using FB.DOMAIN.Authentication;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using System.Data.Entity.Migrations;
namespace FB.DAL.Migrations.ApplicationDbContext
{
internal sealed class Configuration : DbMigrationsConfiguration<DataContexts.ApplicationDbContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
MigrationsDirectory = #"Migrations\ApplicationDbContext";
}
protected override void Seed(DataContexts.ApplicationDbContext context)
{
var roleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(context));
var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context));
if (!roleManager.RoleExists("Admin"))
{
roleManager.Create(new IdentityRole("Admin"));
}
var user = new ApplicationUser { UserName = "james#world.com" };
if (userManager.FindByName("james#world.com") != null) return;
var result = userManager.Create(user, "Password123!");
if (result.Succeeded)
{
userManager.AddToRole(user.Id, "Admin");
}
}
}
}
Shouldn't the above code seed my database with a user? Sadly it doesn't! When I query the AspNetUsers table, it is blank!
What am I doing wrong? :(
I couldn't get two separate contexts talking. So I followed LukeP's solution here.
A single context that houses all my related domain logic.

MVC 5 Seed Users and Roles

I have been playing about with the new MVC 5, I have a few models, controller and views setup using code first migrations.
My question is how do I seed users and roles? I currently seed some reference data in my Seed method in Configuration.cs. But it looks to me that the user and roles tables are not created until something first hits the AccountController.
I currently have two connection strings so I can separate my data from my authentication into different databases.
How can I get the user, roles, etc tables populate along with my others? And not when the account controller is hit?
Here is example of usual Seed approach:
protected override void Seed(SecurityModule.DataContexts.IdentityDb context)
{
if (!context.Roles.Any(r => r.Name == "AppAdmin"))
{
var store = new RoleStore<IdentityRole>(context);
var manager = new RoleManager<IdentityRole>(store);
var role = new IdentityRole { Name = "AppAdmin" };
manager.Create(role);
}
if (!context.Users.Any(u => u.UserName == "founder"))
{
var store = new UserStore<ApplicationUser>(context);
var manager = new UserManager<ApplicationUser>(store);
var user = new ApplicationUser {UserName = "founder"};
manager.Create(user, "ChangeItAsap!");
manager.AddToRole(user.Id, "AppAdmin");
}
}
I used package-manager "update-database". DB and all tables were created and seeded with data.
It's a small addition, but to anyone having the "UserId not found." message when trying to seed: (Tom Regan had this question in the comments, and I was stuck on it myself for a while)
This means that the manager.Create(user, "ChangeItAsap!") was not successful.
This might have a different reason, but for me it was because my password was not succeeding its validation.
I had a custom passwordvalidator, which was not being called when seeding the database, so the validation rules i was used to (minlength 4 instead of default 6) did not apply. Make sure your password (and all other fields for that matter) is passing validation.
This is my method base on Valin answer, I have added roles in db and added password for user. This code is placed in Seed() method in Migrations>Configurations.cs.
// role (Const.getRoles() return string[] whit all roles)
var RoleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(context));
for (int i = 0; i < Const.getRoles().Length; i++)
{
if (RoleManager.RoleExists(Const.getRoles()[i]) == false)
{
RoleManager.Create(new IdentityRole(Const.getRoles()[i]));
}
}
// user
var UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context));
var PasswordHash = new PasswordHasher();
if (!context.Users.Any(u => u.UserName == "admin#admin.net"))
{
var user = new ApplicationUser
{
UserName = "admin#admin.net",
Email = "admin#admin.net",
PasswordHash = PasswordHash.HashPassword("123456")
};
UserManager.Create(user);
UserManager.AddToRole(user.Id, Const.getRoles()[0]);
}
Here i have an very easy,clean and smooth solution.
protected override void Seed(UserContext context)
{
//Step 1 Create the user.
var passwordHasher = new PasswordHasher();
var user = new IdentityUser("Administrator");
user.PasswordHash = passwordHasher.HashPassword("Admin12345");
user.SecurityStamp = Guid.NewGuid().ToString();
//Step 2 Create and add the new Role.
var roleToChoose = new IdentityRole("Admin");
context.Roles.Add(roleToChoose);
//Step 3 Create a role for a user
var role = new IdentityUserRole();
role.RoleId = roleToChoose.Id;
role.UserId = user.Id;
//Step 4 Add the role row and add the user to DB)
user.Roles.Add(role);
context.Users.Add(user);
}
protected override void Seed(ApplicationDbContext context)
{
SeedAsync(context).GetAwaiter().GetResult();
}
private async Task SeedAsync(ApplicationDbContext context)
{
var userManager = new ApplicationUserManager(new UserStore<ApplicationUser, ApplicationRole, int, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>(context));
var roleManager = new ApplicationRoleManager(new RoleStore<ApplicationRole, int, ApplicationUserRole>(context));
if (!roleManager.Roles.Any())
{
await roleManager.CreateAsync(new ApplicationRole { Name = ApplicationRole.AdminRoleName });
await roleManager.CreateAsync(new ApplicationRole { Name = ApplicationRole.AffiliateRoleName });
}
if (!userManager.Users.Any(u => u.UserName == "shimmy"))
{
var user = new ApplicationUser
{
UserName = "shimmy",
Email = "shimmy#gmail.com",
EmailConfirmed = true,
PhoneNumber = "0123456789",
PhoneNumberConfirmed = true
};
await userManager.CreateAsync(user, "****");
await userManager.AddToRoleAsync(user.Id, ApplicationRole.AdminRoleName);
}
}
Looks like they changes the way authentication works in MVC5, changed my Global.asax.cs to the following did the trick!
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
using System.Threading.Tasks;
using MvcAuth.Models;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using System.Threading;
using Microsoft.AspNet.Identity.EntityFramework;
namespace MvcAuth
{
public class MvcApplication : System.Web.HttpApplication
{
async Task<bool> AddRoleAndUser()
{
AuthenticationIdentityManager IdentityManager = new AuthenticationIdentityManager(
new IdentityStore(new ApplicationDbContext()));
var role = new Role("Role1");
IdentityResult result = await IdentityManager.Roles.CreateRoleAsync(role, CancellationToken.None);
if (result.Success == false)
return false;
var user = new ApplicationUser() { UserName = "user1" };
result = await IdentityManager.Users.CreateLocalUserAsync(user, "Password1");
if (result.Success == false)
return false;
result = await IdentityManager.Roles.AddUserToRoleAsync(user.Id, role.Id, CancellationToken.None);
return result.Success;
}
protected async void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
bool x = await AddRoleAndUser();
}
}
}
write this code in your Migration Configuration.
note: Use ApplicationDbContext in Configuration Class.
internal sealed class Configuration : DbMigrationsConfiguration<ApplicationDbContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = false;
}
protected override void Seed(ApplicationDbContext context)
{
// This method will be called after migrating to the latest version.
// You can use the DbSet<T>.AddOrUpdate() helper extension method
// to avoid creating duplicate seed data.
context.Roles.AddOrUpdate(p =>
p.Id,
new IdentityRole { Name = "Admins"},
new IdentityRole { Name = "PowerUsers" },
new IdentityRole { Name = "Users" },
new IdentityRole { Name = "Anonymous" }
);
}
}

Resources