Cannot access HttpContextAccessor User Claims on OnActionExecuting - .net-core

I'm using .net core with JWT for authentication. And I'm trying to access user claims from HttpContextAccessor in my custom attribute. From within the app, I have a UserService.cs (code below) where I can do this. But on OnActionExecuting in my custom attribute, the claims come as an empty error. Even if I call the function from my user service, the claims aren't there.
My ultimate objective is to get the user's id to check if the user has admin access. I don't wanna store the admin access status on the token.
UserService.cs
public AuthenticatedUserClaims AuthenticatedUser()
{
var userClaims = new AuthenticatedUserClaims();
var claims = _contextAccessor.HttpContext.User.Claims;
var enumerable = claims as Claim[] ?? claims.ToArray();
var userId = enumerable.SingleOrDefault(x => x.Type == "UserId")?.Value;
userClaims.UserName = enumerable.SingleOrDefault(x => x.Type == "UserName")?.Value;
userClaims.FullName = enumerable.SingleOrDefault(x => x.Type == "FullName")?.Value;
if (userId != null && !string.IsNullOrEmpty(userId)) userClaims.UserId = int.Parse(userId);
return userClaims;
}
My Custom Attribute
[AttributeUsage(AttributeTargets.Class| AttributeTargets.Method)]
public class PermissionsRequiredAttribute: ActionFilterAttribute
{
private readonly IHttpContextAccessor _contextAccessor;
public PermissionsRequiredAttribute(IHttpContextAccessor contextAccessor)
{
_contextAccessor = contextAccessor;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
var claims = _contextAccessor.HttpContext.User.Claims;
var claimsList = claims as Claim[] ?? claims.ToArray();
// claimsList = Claims[0]??
// context.Result = new UnauthorizedResult();
base.OnActionExecuting(context);
}
}
Attribute Usage
[HttpGet("{id}")]
[ServiceFilter(typeof(PermissionsRequiredAttribute))]
public async Task<ActionResult<Beneficiary>> GetBeneficiary([FromRoute] int id) { //... }
ConfigureServices on Startup.cs
services.AddTransient<IHttpContextAccessor, HttpContextAccessor>();
services.AddScoped<PermissionsRequiredAttribute>();
Thanks in advance :)

I found a "hackaround" based on this answer, but I still don't know why my user HttpContext is empty.
[AttributeUsage(AttributeTargets.Class| AttributeTargets.Method)]
public class PermissionsRequiredAttribute: ActionFilterAttribute
{
private readonly IUser _user;
public PermissionsRequiredAttribute(IUser user)
{
_user = user;
}
public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
bool headers = context.HttpContext.Request.Headers.TryGetValue("Authorization", out var tokens);
string token = tokens.FirstOrDefault()?.Split(" ")[1];
if (string.IsNullOrEmpty(token))
context.Result = new UnauthorizedResult();
var handler = new JwtSecurityTokenHandler();
JwtSecurityToken securityToken = (JwtSecurityToken) handler.ReadToken(token);
IEnumerable<Claim> claims = securityToken.Payload.Claims;
string userId = claims.SingleOrDefault(c => c.Type == "UserId")?.Value;
if (!string.IsNullOrEmpty(userId))
context.Result = new UnauthorizedResult();
var currentUser = await _user.GetUser(int.Parse(userId ?? throw new UnauthorizedAccessException()));
if (!currentUser.isAdmin)
context.Result = new UnauthorizedResult();
await base.OnActionExecutionAsync(context, next);
}
}
EDIT: 16-10-2021
I found a solution I'm happy with, it looks like I just needed to add the line in Startup.cs in ConfigureServices(IServiceCollection services)
services.AddHttpContextAccessor();
And now the line below has values Claims
var claims = _contextAccessor.HttpContext.User.Claims;

Related

Authentication and LoginPath for different areas in ASP.NET Core 2

ASP.NET Core 2
Help me to configure AddAuthentication for two routes: users (user accounts) and admin area.
For example, if user doesn't signed in and trying to enter /Account/Orders/ he'll be redirected to /Account/SignIn/.
But if someone trying access /Admin/Orders/ must be redireted to /Admin/Signin/
Have not found ay solution ATM.
Solved!
In admin area (controllers) we using Authorize attr. arg.: [Authorize(AuthenticationSchemes = "backend")] and that is.
BTW we are able to make any tuning by accessing HttpContext in AddCookie's options and events.
Configuration:
services
.AddAuthentication(o =>
{
o.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, o =>
{
o.LoginPath = new PathString("/account/login/");
})
.AddCookie("backend", o =>
{
o.LoginPath = new PathString("/admin/account/login/");
});
#Alex's answer got me 90% of the way there.
In .Net 6, I'm using this https://learn.microsoft.com/en-us/aspnet/core/security/authentication/cookie?view=aspnetcore-6.0 approach to use Cookies without using the Username setup in identity.
Program.cs
var authentication = services.AddAuthentication(o =>
{
o.DefaultScheme = AuthenticationSchemes.FrontEnd;
});
authentication.AddCookie(AuthenticationSchemes.FrontEnd, o =>
{
o.LoginPath = CookieAuthenticationDefaults.LoginPath;
});
authentication.AddCookie(AuthenticationSchemes.BackEnd, o =>
{
o.LoginPath = new PathString("/admin/login/");
o.AccessDeniedPath = new PathString("/admin/accessdenied");
});
AppAuthenticationSchemes.cs
public class AuthenticationSchemes
{
public const string FrontEnd = "Frontend";
public const string BackEnd = "Backend";
public const string Either = FrontEnd + "," + BackEnd;
}
AccountController.cs
[AllowAnonymous]
public class AccountController : Controller
{
private readonly FrontEndSecurityManager _frontEndSecurityManager;
public AccountController(FrontEndSecurityManager frontEndSecurityManager)
{
_frontEndSecurityManager = frontEndSecurityManager;
}
[HttpPost(Name = "Login")]
public async Task<ActionResult> Login(LoginViewModel loginModel)
{
if (string.IsNullOrEmpty(loginModel.Username) ||
string.IsNullOrEmpty(loginModel.Password))
{
ModelState.AddModelError("form", "Please enter Username and Password");
return RedirectToAction("Login", "Account");
}
var loginResult = await _frontEndSecurityManager.ValidateCredentials(loginModel.Username, loginModel.Password);
if (!loginResult.IsSuccess)
{
this.AddFlash(FlashMessageType.Danger, "UserName or Password is incorrect");
return RedirectToAction("Login", "Account");
}
var identity = await _frontEndSecurityManager.CreateIdentityAsync(loginModel.Username, loginResult);
await _frontEndSecurityManager.SignInAsync(identity, HttpContext);
return RedirectToAction("Menu", "App");
}
}
FrontEndSecurityManager.cs
public class FrontEndSecurityManager
{
private readonly SignInApi _api;
private readonly AuthenticationOptions _authenticationOptions;
public FrontEndSecurityManager(SignInApi api, IOptions<AuthenticationOptions> authenticationOptions)
{
_api = api;
_authenticationOptions = authenticationOptions.Value;
}
public async Task SignInAsync(ClaimsIdentity identity, HttpContext httpContext)
{
var authProperties = new AuthenticationProperties
{
AllowRefresh = true,
ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(30),
IsPersistent = true,
IssuedUtc = DateTimeOffset.UtcNow
};
await httpContext.SignOutAsync(AuthenticationSchemes.BackEnd);
await httpContext.SignInAsync(AuthenticationSchemes.FrontEnd, new ClaimsPrincipal(identity), authProperties);
}
public async Task<LoginResult> ValidateCredentials(string username, string password)
{
if (_authenticationOptions.DemoUserEnabled)
{
if (string.Equals(username, "demo", StringComparison.InvariantCultureIgnoreCase))
{
var result = new LoginResult(StandardResults.SuccessResult, "")
{
Employee_Name = "Demo User",
Employee_Email = "DemoGuy#gmail.com",
Employee_Initials = "DG",
Employee_Type = "Regular",
Role1 = true,
Role2 = true,
Role3 = false
};
return result;
}
}
var apiRequest = new LoginRequest() { Username = username, Password = password };
var loginResult = await _api.LoginAsync(apiRequest);
if (loginResult.Success)
{
return loginResult.Data;
}
else
{
return LoginResult.Failure();
}
}
public async Task<ClaimsIdentity> CreateIdentityAsync(string username, LoginResult loginResult)
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, username),
new Claim(ClaimTypes.Role, AppRoles.User),
new Claim(ClaimTypes.Email, loginResult.Employee_Email, ClaimValueTypes.Email),
new Claim(ClaimTypes.GivenName, loginResult.GivenName),
new Claim(ClaimTypes.Surname, loginResult.Surname),
new Claim(AppClaimTypes.EmployeeType, loginResult.Employee_Type),
new Claim(AppClaimTypes.EmployeeInitials, loginResult.Employee_Initials),
new Claim(AppClaimTypes.Location, loginResult.Location.ToString(), ClaimValueTypes.Integer),
};
if (loginResult.Use_Checkin)
{
claims.Add(new Claim(ClaimTypes.Role, AppRoles.Checkin));
}
if (loginResult.Use_Pickup)
{
claims.Add(new Claim(ClaimTypes.Role, AppRoles.Pickup));
}
var identity = new ClaimsIdentity(claims, AuthenticationSchemes.FrontEnd);
return identity;
}
public void SignOutAsync(HttpContext httpContext)
{
httpContext.SignOutAsync(AuthenticationSchemes.FrontEnd);
}
}
From here, you could easily extrapolate how you want the back-end authentication controller to work. Essentially something like
await HttpContext.SignOutAsync(AuthenticationSchemes.FrontEnd);await HttpContext.SignInAsync(AuthenticationSchemes.BackEnd, new ClaimsPrincipal(identity), authProperties);
An example of using each policy would be
[Authorize(AuthenticationSchemes = AuthenticationSchemes.BackEnd)]
public IActionResult Secure()
{
return View("Secure");
}
or
[Authorize] // scheme not explicit, so Pr DefaultScheme = AuthenticationSchemes.FrontEnd is used
[Route("[controller]")]
public class OutgoingController : Controller
{
}

Net Core 2.0 API setting up authorization and authentication

I have a .net core application that I have upgraded from 1.1 to 2.0.
The problem I am having is working out how to set up both authentication and authorization.
I am getting this exception when I am trying to hit an api endpoint...
2017-08-15 15:28:12.2191|13|Microsoft.AspNetCore.Server.Kestrel|ERROR|
Connection id "0HL73T7CAJGBE", Request id "0HL73T7CAJGBE:00000001": An
unhandled exception was thrown by the application. No
authenticationScheme was specified, and there was no
DefaultChallengeScheme found.
My controller has this attribute on it...
[Authorize(Policy = "Viewer3AuthPolicy")]
My startup.cs has this method to try and set everything up...
public void ConfigureServices(IServiceCollection services)
{
SetCorsPolicy(services);
services.AddMvc();
services.AddAuthentication(o =>
{
o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
});
SetAuthorisationPolicy(services);
services.AddAuthorization(options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme).RequireAuthenticatedUser().Build();
options.AddPolicy("Viewer3AuthPolicy",
policy => policy.RequireClaim(Constants.AuthPolicyClaimsName, Constants.AuthPolicyClaimsValue));
});
}
In my Configuremethod I am calling...
app.UseAuthentication();
I am thinking that I must have some ordering wrong or am making the wrong calls in the setup.
Does anyone have any ideas?
Solution with _userManager.AddClaimsAsync. Here is the simplified version of changes I made under ConfigureServices:
services.AddAuthorization(options => {
options.AddPolicy("CRM", policy => { policy.RequireClaim("department", "Sales", "Customer Service", "Marketing", "Advertising", "MIS"); });
});
AccountController constructor:
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly IEmailSender _emailSender;
private readonly ILogger _logger;
private readonly MyDB_Context _context;
public AccountController(
MyDB_Context context,
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
IEmailSender emailSender,
ILogger<AccountController> logger)
{
_context = context;
_userManager = userManager;
_signInManager = signInManager;
_emailSender = emailSender;
_logger = logger;
}
Under LogIn:
(var vUser is my own class with properties Name, department, SingIn, etc...). The sample below uses combination of custom user table mytable (to read from claim types and their values) and AspNetUserClaims table (to add claims):
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid) {
var vUser = _context.mytable.SingleOrDefault(m => m.Email.ToUpper() == model.Email.ToUpper());
const string Issuer = "https://www.mycompany.com/";
var user = _userManager.Users.Where(u => u.Email == model.Email).FirstOrDefault();
ApplicationUser applicationUser = await _userManager.FindByNameAsync(user.UserName);
IList<Claim> allClaims = await _userManager.GetClaimsAsync(applicationUser); // get all the user claims
// Add claim if missing
if (allClaims.Where(c => c.Type == "department" && c.Value == vUser.department).ToList().Count == 0) {
await _userManager.AddClaimAsync(user, new Claim("department", vUser.department, ClaimValueTypes.String, Issuer));
}
// Remove all other claim values for "department" type
var dept = allClaims.Where(c => c.Type == "department" && c.Value != vUser.department);
foreach(var claim in dept) {
await _userManager.RemoveClaimAsync(user, new Claim("department", claim.Value, ClaimValueTypes.String, Issuer));
}
vUser.SignIn = DateTime.Now; _context.Update(vUser); await _context.SaveChangesAsync();
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, set lockoutOnFailure: true
var result = await _signInManager.PasswordSignInAsync(vUser.Name, model.Password, model.RememberMe, lockoutOnFailure: false);
//var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);
if (result.Succeeded) {
_logger.LogInformation("User logged in.");
return RedirectToLocal(returnUrl);
}
if (result.RequiresTwoFactor) {
return RedirectToAction(nameof(LoginWith2fa), new { returnUrl, model.RememberMe });
}
if (result.IsLockedOut) {
_logger.LogWarning("User account locked out.");
return RedirectToAction(nameof(Lockout));
} else {
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return View(model);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
This is what I have in my controller:
[Authorize(Policy = "CRM")]
public class CRMController : Controller
Adding
AddJwtBearer(options => { options.RequireHttpsMetadata = false; ... });"
did the trick for me.
[dotnet core 2.0]

I want web (MVC) and API on same database

I have a database, need web interface for administration and management (number of transactions, billing, and other administration) and to serve data from database (products) "manually" and API to serve data (products) to other bigger clients. All secured by SSL and https obviously.
I made a asp.net MVC 5 app (business logic and administration) and wanted to implement API (noob in API) for delivering data to users.
Have no idea how to implement security from MVC to API(same database).
The app is small and I can rewrite it. I'm thinking to try with core, but fear that i will be stuck with same problem.
Concrete question: What approach do I take and weather it should be in MVC 5 generation or .core (MVC 6) to be able to use one database for data, users and their authorizations?
(pushing everything true API is something I would like to avoid)
Ok, my project is done. I moved ahead on MVC 5.
(I apologize to you perfectionists, but I don't have the time now to strip unnecessary thing so I dumped whole files as they are :)
1st approach - abandoned
First I tried designing it as is recommended true the internet: .MVC solution, .DB for database and .API solution.
Conclusion - A lot of problems wit authentication and entity framework. At the end I abandoned this approach
2nd and successful approach
Just one solution .MVC
True NuGet installed .net Api, used integrated authorization extended using few tutorials (not single one worked). Note that I use ASP.NET Identity 2.0 Extending Identity Models and Using Integer Keys Instead of Strings and Implementing HTTPS Everywhere in ASP.Net MVC application.
Here are modifications and addons:
App_Start -> IdentityConfig.cs
public class ApplicationUserManager : UserManager<ApplicationUser, int>
{
// *** ADD INT TYPE ARGUMENT TO CONSTRUCTOR CALL:
public ApplicationUserManager(IUserStore<ApplicationUser, int> store)
: base(store)
{
}
public static ApplicationUserManager Create(
IdentityFactoryOptions<ApplicationUserManager> options,
IOwinContext context)
{
// *** PASS CUSTOM APPLICATION USER STORE AS CONSTRUCTOR ARGUMENT:
var manager = new ApplicationUserManager(
new ApplicationUserStore(context.Get<ApplicationDbContext>()));
// Configure validation logic for usernames
// *** ADD INT TYPE ARGUMENT TO METHOD CALL:
manager.UserValidator = new UserValidator<ApplicationUser, int>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
// Configure validation logic for passwords
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = false,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
};
// other code removed for brevity
manager.UserLockoutEnabledByDefault = Convert.ToBoolean(ConfigurationManager.AppSettings["UserLockoutEnabledByDefault"].ToString());
manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(Double.Parse(ConfigurationManager.AppSettings["DefaultAccountLockoutTimeSpan"].ToString()));
manager.MaxFailedAccessAttemptsBeforeLockout = Convert.ToInt32(ConfigurationManager.AppSettings["MaxFailedAccessAttemptsBeforeLockout"].ToString());
// Register two factor authentication providers.
// This application uses Phone and Emails as a step of receiving a
// code for verifying the user You can write your own provider and plug in here.
// *** ADD INT TYPE ARGUMENT TO METHOD CALL:
//manager.RegisterTwoFactorProvider("PhoneCode",
// new PhoneNumberTokenProvider<ApplicationUser, int>
// {
// MessageFormat = "Your security code is: {0}"
// });
//// *** ADD INT TYPE ARGUMENT TO METHOD CALL:
//manager.RegisterTwoFactorProvider("EmailCode",
// new EmailTokenProvider<ApplicationUser, int>
// {
// Subject = "SecurityCode",
// BodyFormat = "Your security code is {0}"
// });
//manager.EmailService = new EmailService();
//manager.SmsService = new SmsService();
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
// *** ADD INT TYPE ARGUMENT TO METHOD CALL:
manager.UserTokenProvider =
new DataProtectorTokenProvider<ApplicationUser, int>(
dataProtectionProvider.Create("ASP.NET Identity"));
}
return manager;
}
}
// PASS CUSTOM APPLICATION ROLE AND INT AS TYPE ARGUMENTS TO BASE:
public class ApplicationRoleManager : RoleManager<ApplicationRole, int>
{
// PASS CUSTOM APPLICATION ROLE AND INT AS TYPE ARGUMENTS TO CONSTRUCTOR:
public ApplicationRoleManager(IRoleStore<ApplicationRole, int> roleStore)
: base(roleStore)
{
}
// PASS CUSTOM APPLICATION ROLE AS TYPE ARGUMENT:
public static ApplicationRoleManager Create(
IdentityFactoryOptions<ApplicationRoleManager> options, IOwinContext context)
{
return new ApplicationRoleManager(
new ApplicationRoleStore(context.Get<ApplicationDbContext>()));
}
}
public class EmailService : IIdentityMessageService
{
public Task SendAsync(IdentityMessage message)
{
// Plug in your email service here to send an email.
return Task.FromResult(0);
}
}
public class SmsService : IIdentityMessageService
{
public Task SendAsync(IdentityMessage message)
{
// Plug in your sms service here to send a text message.
return Task.FromResult(0);
}
}
//This is useful if you do not want to tear down the database each time you run the application.
//public class ApplicationDbInitializer : DropCreateDatabaseAlways<ApplicationDbContext>
//This example shows you how to create a new database if the Model changes
public class ApplicationDbInitializer : DropCreateDatabaseIfModelChanges<ApplicationDbContext>
{
protected override void Seed(ApplicationDbContext context)
{
//InitializeIdentityForEF(context); //- Do not Seed - IGOR
//base.Seed(context);
}
//Create User=Admin#Admin.com with password=Admin#123456 in the Admin role
//public static void InitializeIdentityForEF(ApplicationDbContext db)
//{
// var userManager = HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();
// var roleManager = HttpContext.Current.GetOwinContext().Get<ApplicationRoleManager>();
// const string name = "igor#email.mail";
// const string password = "LolLol1";
// const string roleName = "lol";
// //Create Role Admin if it does not exist
// var role = roleManager.FindByName(roleName);
// if (role == null)
// {
// role = new ApplicationRole(roleName);
// var roleresult = roleManager.Create(role);
// }
// var user = userManager.FindByName(name);
// if (user == null)
// {
// user = new ApplicationUser { UserName = name, Email = name };
// var result = userManager.Create(user, password);
// result = userManager.SetLockoutEnabled(user.Id, false);
// }
// // Add user admin to Role Admin if not already added
// var rolesForUser = userManager.GetRoles(user.Id);
// if (!rolesForUser.Contains(role.Name))
// {
// var result = userManager.AddToRole(user.Id, role.Name);
// }
//}
}
public class ApplicationSignInManager : SignInManager<ApplicationUser, int>
{
public ApplicationSignInManager(ApplicationUserManager userManager, IAuthenticationManager authenticationManager) :
base(userManager, authenticationManager)
{ }
public override Task<ClaimsIdentity> CreateUserIdentityAsync(ApplicationUser user)
{
return user.GenerateUserIdentityAsync((ApplicationUserManager)UserManager);
}
public static ApplicationSignInManager Create(IdentityFactoryOptions<ApplicationSignInManager> options, IOwinContext context)
{
return new ApplicationSignInManager(context.GetUserManager<ApplicationUserManager>(), context.Authentication);
}
}
App_Start -> Startup.Auth.cs
public partial class Startup
{
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
public static string PublicClientId { get; private set; }
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context, user manager and role manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
// Configure the sign in cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser, int>(
validateInterval: TimeSpan.FromMinutes(2880),
regenerateIdentityCallback: (manager, user) => user.GenerateUserIdentityAsync(manager),
// Need to add THIS line because we added the third type argument (int) above:
getUserIdCallback: (claim) => int.Parse(claim.GetUserId()))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Enables the application to temporarily store user information when they are verifying the second factor in the two-factor authentication process.
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
// Enables the application to remember the second login verification factor such as phone or email.
// Once you check this option, your second step of verification during the login process will be remembered on the device where you logged in from.
// This is similar to the RememberMe option when you log in.
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
// Uncomment the following lines to enable logging in with third party login providers
//app.UseMicrosoftAccountAuthentication(
// clientId: "",
// clientSecret: "");
//app.UseTwitterAuthentication(
// consumerKey: "",
// consumerSecret: "");
//app.UseFacebookAuthentication(
// appId: "",
// appSecret: "");
//app.UseGoogleAuthentication(
// clientId: "",
// clientSecret: "");
// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"), //TODO - makni ovo
AccessTokenExpireTimeSpan = TimeSpan.FromHours(1),
// In production mode set AllowInsecureHttp = false
AllowInsecureHttp = true
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
}
}
//public partial class Startup
//{
// public void ConfigureAuth(IAppBuilder app)
// {
// // Enable the application to use a cookie to store information for the signed in user
// app.UseCookieAuthentication(new CookieAuthenticationOptions
// {
// ExpireTimeSpan = TimeSpan.FromHours(24),
// CookieSecure = CookieSecureOption.Never,
// CookieHttpOnly = false,
// SlidingExpiration = true,
// AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
// LoginPath = new PathString("/Account/Login")
// });
// // Use a cookie to temporarily store information about a user logging in with a third party login provider
// app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// }
//}
App_Start -> WebApiConfig.cs
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// TODO: Add any additional configuration code.
// Web API routes
config.MapHttpAttributeRoutes();
//config.Routes.MapHttpRoute(
// name: "getkey",
// routeTemplate: "api/ApiKeys/Get/{term}"
//);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
// WebAPI when dealing with JSON & JavaScript!
// Setup json serialization to serialize classes to camel (std. Json format)
var formatter = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
formatter.SerializerSettings.ContractResolver =
new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver();
// make all web-api requests to be sent over https
config.MessageHandlers.Add(new EnforceHttpsHandler());
}
}
MySysAdmin controller that I use for initial insert and edit of roles and initial user.
public SysAdminController(ApplicationUserManager userManager,
ApplicationRoleManager roleManager)
{
UserManager = userManager;
RoleManager = roleManager;
}
private ApplicationUserManager _userManager;
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
set
{
_userManager = value;
}
}
private ApplicationRoleManager _roleManager;
public ApplicationRoleManager RoleManager
{
get
{
return _roleManager ?? HttpContext.GetOwinContext().Get<ApplicationRoleManager>();
}
private set
{
_roleManager = value;
}
}
public ActionResult RoleIndex()
{
return View(RoleManager.Roles);
}
public ActionResult RoleCreate()
{
return View();
}
[HttpPost]
public async Task<ActionResult> RoleCreate(SysAdminVM.RoleViewModel roleViewModel)
{
if (ModelState.IsValid)
{
// Use ApplicationRole, not IdentityRole:
var role = new ApplicationRole(roleViewModel.Name);
var roleresult = await RoleManager.CreateAsync(role);
if (!roleresult.Succeeded)
{
ModelState.AddModelError("", roleresult.Errors.First());
return View();
}
return RedirectToAction("RoleIndex");
}
return View();
}
public async Task<ActionResult> RoleEdit(int id)
{
if (id > 0)
{
var role = await RoleManager.FindByIdAsync(id);
if (role == null)
{
return HttpNotFound();
}
SysAdminVM.RoleViewModel roleModel = new SysAdminVM.RoleViewModel { Id = role.Id, Name = role.Name };
return View(roleModel);
}
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> RoleEdit([Bind(Include = "Name,Id")] SysAdminVM.RoleViewModel roleModel)
{
if (ModelState.IsValid)
{
var role = await RoleManager.FindByIdAsync(roleModel.Id);
role.Name = roleModel.Name;
await RoleManager.UpdateAsync(role);
return RedirectToAction("RoleIndex");
}
return View();
}
[AllowAnonymous]
public async Task<ActionResult> Initialize()
{
if (db.App.Where(x => x.Name.Contains("Initialize")).FirstOrDefault() == null)
{
await InitRoleCreate();
await InitUser();
db.App.Add(
new App { Name = "Initialize", Val = "true" }
);
db.SaveChanges();
return View();
}
return HttpNotFound();
}
private async Task InitRoleCreate()
{
var model = new List<string>()
{
"SysAdmin",
"Admin",
"User",
};
foreach (var item in model)
{
var role = new ApplicationRole(item);
await RoleManager.CreateAsync(role);
}
}
private async Task InitUser()
{
var user = new ApplicationUser
{
UserName = "HerGiz",
Email = "hergiz#outlook.com",
Name = "Igor Hermanović",
Contact = "098 185 3131",
TwoFactorEnabled = false,
LockoutEnabled = true,
EmailConfirmed = true
};
var adminResult = await UserManager.CreateAsync(user, "W7xtc2ywfb");
await UserManager.AddToRolesAsync(user.Id, "SysAdmin");
}
}
Entire API part that I need - controller and out of the box login (that is berried somewhere):
[Authorize]
public class ApiKeysController : ApiController
{
[Authorize]
[Route("api/getkey/{term}")]
public ShowFullKeyVM Get(string term)
{
if (User.Identity.IsAuthenticated == true)
{
if (!string.IsNullOrWhiteSpace(term) && (term.Length == 15 || term.Length == 16))
{
var lKey = new LKey();
var vm = lKey.Search(term);
if (vm != null)
{
return vm;
}
}
return new ShowFullKeyVM() { Error = "IMEI either is not valid :(", SearchIMEI = term };
}
return new ShowFullKeyVM() { Error = "Not Authenticated!!!", SearchIMEI = term };
}
}
Global.asax
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
MvcHandler.DisableMvcResponseHeader = true;
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
App_Start.AutoMapperConfig.DefineMaps();
ModelBinders.Binders.Add(typeof(decimal), new Extensions.DecimalModelBinder());
ModelBinders.Binders.Add(typeof(decimal?), new Extensions.DecimalModelBinder());
}
Web.config
<appSettings>
<add key="UserLockoutEnabledByDefault" value="true" />
<add key="DefaultAccountLockoutTimeSpan" value="30" />
<add key="MaxFailedAccessAttemptsBeforeLockout" value="4" />
</appSettings>

Store data in cookie with asp.net core identity

I'm using ASP.NET core identity with EF end I would like to store data related to the user in the authentication cookie.
This is how I used to do with ASP.NET 4.6 (appcontext is the data to store):
public static void IdentitySignin(AppContext appContext, string providerKey = null, bool isPersistent = false)
{
var claims = new List<Claim>();
// create *required* claims
claims.Add(new Claim(ClaimTypes.NameIdentifier, appContext.UserId.ToString()));
claims.Add(new Claim(ClaimTypes.Name, appContext.UserName));
// serialized AppUserState object
claims.Add(new Claim("appcontext" + EZA.Store.AppContext.Version, appContext.ToString()));
var identity = new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie);
// add to user here!
AuthenticationManager.SignIn(new AuthenticationProperties()
{
AllowRefresh = true,
IsPersistent = isPersistent,
ExpiresUtc = DateTime.UtcNow.AddDays(7),
}, identity);
}
but now I'm using ASP.NET Identity with EF and I can't find a way to store some data in the cookie.
Use AddClaimsAsync or AddClaimAsync of UserManager<YourUserIdentity>. for exemple like this when you sign in your user:
public class AccountController : Controller
{
public UserManager<YourUserIdentity> UserManager { get; private set; }
public SignInManager<YourUserIdentity> SignInManager { get; private set; }
public AccountController(UserManager<YourUserIdentity> userManager,
SignInManager<YourUserIdentity> signInManager)
{
UserManager = userManager;
SignInManager = signInManager;
}
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
if (ModelState.IsValid)
{
var user = await UserManager.FindByNameAsync(model.UserName);
await UserManager.AddClaimAsync(user, new Claim("your-claim", "your-value"));
var signInStatus = await SignInManager.PasswordSignInAsync(user, model.Password, model.RememberMe, lockoutOnFailure: false);
if (signInStatus.Succeeded)
return RedirectToLocal(returnUrl);
ModelState.AddModelError("", "Invalid username or password.");
return View(model);
}
// If we got this far, something failed, redisplay form
return View("Index", new LoginPageViewModel() { Login = model });
}
}
Before i read #aqua's answer(i have learned this way just now), i would say that you have two options:
1 - Overriding UserClaimsPrincipalFactory like below:
public class AppClaimsPrincipalFactory : UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>
{
public AppClaimsPrincipalFactory(
UserManager<ApplicationUser> userManager,
RoleManager<IdentityRole> roleManager,
IOptions<IdentityOptions> optionsAccessor) : base(userManager, roleManager, optionsAccessor)
{
}
public async override Task<ClaimsPrincipal> CreateAsync(ApplicationUser user)
{
var principal = await base.CreateAsync(user);
((ClaimsIdentity)principal.Identity).AddClaims(new[] {
new Claim("<claim name>", value)
});
return principal;
}
}
// register it
services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, AppClaimsPrincipalFactory>();
2- Using OnSigningIn event.
services.Configure<IdentityOptions>(opt =>
{
opt.Cookies.ApplicationCookie.Events = new CookieAuthenticationEvents()
{
OnSigningIn = async (context) =>
{
ClaimsIdentity identity = (ClaimsIdentity)context.Principal.Identity;
identity.AddClaim(new Claim("<claim name>", value));
}
};
});

How to Seed Users and Roles with Code First Migration using Identity ASP.NET Core

I have created a new clean asp.net 5 project (rc1-final). Using Identity Authentication I just have the ApplicationDbContext.cs with the following code:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
protected override void OnModelCreating(ModelBuilder builder)
{
// On event model creating
base.OnModelCreating(builder);
}
}
Please note ApplicationDbContext use IdentityDbContext and not DbContext.
There is any IdentityConfig.cs. Where i need to put the classic protected override void Seed to create role and user if it does not exist?
My way of doing this is to create a class in models namespace.
public class SampleData
{
public static void Initialize(IServiceProvider serviceProvider)
{
var context = serviceProvider.GetService<ApplicationDbContext>();
string[] roles = new string[] { "Owner", "Administrator", "Manager", "Editor", "Buyer", "Business", "Seller", "Subscriber" };
foreach (string role in roles)
{
var roleStore = new RoleStore<IdentityRole>(context);
if (!context.Roles.Any(r => r.Name == role))
{
roleStore.CreateAsync(new IdentityRole(role));
}
}
var user = new ApplicationUser
{
FirstName = "XXXX",
LastName = "XXXX",
Email = "xxxx#example.com",
NormalizedEmail = "XXXX#EXAMPLE.COM",
UserName = "Owner",
NormalizedUserName = "OWNER",
PhoneNumber = "+111111111111",
EmailConfirmed = true,
PhoneNumberConfirmed = true,
SecurityStamp = Guid.NewGuid().ToString("D")
};
if (!context.Users.Any(u => u.UserName == user.UserName))
{
var password = new PasswordHasher<ApplicationUser>();
var hashed = password.HashPassword(user,"secret");
user.PasswordHash = hashed;
var userStore = new UserStore<ApplicationUser>(context);
var result = userStore.CreateAsync(user);
}
AssignRoles(serviceProvider, user.Email, roles);
context.SaveChangesAsync();
}
public static async Task<IdentityResult> AssignRoles(IServiceProvider services, string email, string[] roles)
{
UserManager<ApplicationUser> _userManager = services.GetService<UserManager<ApplicationUser>>();
ApplicationUser user = await _userManager.FindByEmailAsync(email);
var result = await _userManager.AddToRolesAsync(user, roles);
return result;
}
}
To run this code on startup. In Startup.cs at end of configure method just after route configuration add following code as Stafford Williams said before.
SampleData.Initialize(app.ApplicationServices);
You can seed Users and Roles in OnModelCreating() method inside IdentityDbContext.cs file as shown below. Notice that the keys have to be predefined to avoid seeding new users and roles everytime this method is executed.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
//Seeding a 'Administrator' role to AspNetRoles table
modelBuilder.Entity<IdentityRole>().HasData(new IdentityRole {Id = "2c5e174e-3b0e-446f-86af-483d56fd7210", Name = "Administrator", NormalizedName = "ADMINISTRATOR".ToUpper() });
//a hasher to hash the password before seeding the user to the db
var hasher = new PasswordHasher<IdentityUser>();
//Seeding the User to AspNetUsers table
modelBuilder.Entity<IdentityUser>().HasData(
new IdentityUser
{
Id = "8e445865-a24d-4543-a6c6-9443d048cdb9", // primary key
UserName = "myuser",
NormalizedUserName = "MYUSER",
PasswordHash = hasher.HashPassword(null, "Pa$$w0rd")
}
);
//Seeding the relation between our user and role to AspNetUserRoles table
modelBuilder.Entity<IdentityUserRole<string>>().HasData(
new IdentityUserRole<string>
{
RoleId = "2c5e174e-3b0e-446f-86af-483d56fd7210",
UserId = "8e445865-a24d-4543-a6c6-9443d048cdb9"
}
);
}
As of the time of this writing, there is no plug in place for seeding the database, but you can create a class and add it to your container to do the same thing on app start, here is how I've done it, first create a class:
public class YourDbContextSeedData
{
private YourDbContext _context;
public YourDbContextSeedData(YourDbContext context)
{
_context = context;
}
public async void SeedAdminUser()
{
var user = new ApplicationUser
{
UserName = "Email#email.com",
NormalizedUserName = "email#email.com",
Email = "Email#email.com",
NormalizedEmail = "email#email.com",
EmailConfirmed = true,
LockoutEnabled = false,
SecurityStamp = Guid.NewGuid().ToString()
};
var roleStore = new RoleStore<IdentityRole>(_context);
if (!_context.Roles.Any(r => r.Name == "admin"))
{
await roleStore.CreateAsync(new IdentityRole { Name = "admin", NormalizedName = "admin" });
}
if (!_context.Users.Any(u => u.UserName == user.UserName))
{
var password = new PasswordHasher<ApplicationUser>();
var hashed = password.HashPassword(user, "password");
user.PasswordHash = hashed;
var userStore = new UserStore<ApplicationUser>(_context);
await userStore.CreateAsync(user);
await userStore.AddToRoleAsync(user, "admin");
}
await _context.SaveChangesAsync();
}
Register the type in ConfigureServices method of your Startup.cs class:
services.AddTransient<YourDbContextSeedData>();
Next pass the YourDbContextSeedData class to the Configure method of your Startup.cs class and use it:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, YourDbContextSeedData seeder)
{
seeder.SeedAdminUser();
}
If you have async issues, try the following code:
protected override void Seed(ApplicationDbContext context)
{
// This method will be called after migrating to the latest version.
string[] roles = new string[] { "Admin", "User" };
foreach (string role in roles)
{
if (!context.Roles.Any(r => r.Name == role))
{
context.Roles.Add(new IdentityRole(role));
}
}
//create user UserName:Owner Role:Admin
if (!context.Users.Any(u => u.UserName == "Owner"))
{
var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context));
var user = new ApplicationUser
{
FirstName = "XXXX",
LastName = "XXXX",
Email = "xxxx#example.com",
UserName = "Owner",
PhoneNumber = "+111111111111",
EmailConfirmed = true,
PhoneNumberConfirmed = true,
SecurityStamp = Guid.NewGuid().ToString("D"),
PasswordHash = userManager.PasswordHasher.HashPassword("secret"),
LockoutEnabled = true,
};
userManager.Create(user);
userManager.AddToRole(user.Id, "Admin");
}
context.SaveChanges();
}
In aspnetcore there is the concept of IHostedService. This makes it possible to run async background Task.
The solution of #hamid-mosalla could be made async and called from an IHostedService implementation.
Seed class implementation could be something like
public class IdentityDataSeeder
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly RoleManager<IdentityRole> _roleManager;
public IdentityDataSeeder(
UserManager<ApplicationUser> userManager,
RoleManager<IdentityRole> roleManager)
{
_userManager = userManager;
_roleManager = roleManager;
}
public async Task SeedAsync()
{
var superAdminRole = new IdentityRole
{
Id = "cac43a6e-f7bb-4448-baaf-1add431ccbbf",
Name = "SuperAdmin",
NormalizedName = "SUPERADMIN"
};
await CreateRoleAsync(superAdminRole);
var superAdminUserPassword = "P#ssword1";
var superAdminUser = new ApplicationUser
{
Id = "b8633e2d-a33b-45e6-8329-1958b3252bbd",
UserName = "admin#example.nl",
NormalizedUserName = "ADMIN#EXAMPLE.NL",
Email = "admin#example.nl",
NormalizedEmail = "ADMIN#EXAMPLE.NL",
EmailConfirmed = true,
};
await CreateUserAsync(superAdminUser, superAdminUserPassword);
var superAdminInRole = await _userManager.IsInRoleAsync(superAdminUser, superAdminRole.Name);
if (!superAdminInRole)
await _userManager.AddToRoleAsync(superAdminUser, superAdminRole.Name);
}
private async Task CreateRoleAsync(IdentityRole role)
{
var exits = await _roleManager.RoleExistsAsync(role.Name);
if (!exits)
await _roleManager.CreateAsync(role);
}
private async Task CreateUserAsync(ApplicationUser user, string password)
{
var exists = await _userManager.FindByEmailAsync(user.Email);
if (exists == null)
await _userManager.CreateAsync(user, password);
}
}
This can be called from an IHostedService:
public class SetupIdentityDataSeeder : IHostedService
{
private readonly IServiceProvider _serviceProvider;
public SetupIdentityDataSeeder(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
using (var scope = _serviceProvider.CreateScope())
{
var seeder = scope.ServiceProvider.GetRequiredService<IdentityDataSeeder>();
await seeder.SeedAsync();
}
}
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}
Startup would look like:
public void ConfigureServices(IServiceCollection services)
{
//...
services.AddHostedService<SetupIdentityDataSeeder>();
}
This is not yet implemented. As a work around, just write your own class that will check the database for the existence of your entities, add them if they don't exist, and call this class from your Startup.cs.
My way:
Create Class in models folder
public static class ModelBuilderExtensions
{
public static void Seed(this ModelBuilder builder)
{
// Seed Roles
List<IdentityRole> roles = new List<IdentityRole>()
{
new IdentityRole { Name = "Admin", NormalizedName = "ADMIN" },
new IdentityRole { Name = "User", NormalizedName = "USER" }
};
builder.Entity<IdentityRole>().HasData(roles);
// -----------------------------------------------------------------------------
// Seed Users
var passwordHasher = new PasswordHasher<ApplicationUser>();
List<ApplicationUser> users = new List<ApplicationUser>()
{
// imporant: don't forget NormalizedUserName, NormalizedEmail
new ApplicationUser {
UserName = "user2#hotmail.com",
NormalizedUserName = "USER2#HOTMAIL.COM",
Email = "user2#hotmail.com",
NormalizedEmail = "USER2#HOTMAIL.COM",
},
new ApplicationUser {
UserName = "user3#hotmail.com",
NormalizedUserName = "USER3#HOTMAIL.COM",
Email = "user3#hotmail.com",
NormalizedEmail = "USER3#HOTMAIL.COM",
},
};
builder.Entity<ApplicationUser>().HasData(users);
///----------------------------------------------------
// Seed UserRoles
List<IdentityUserRole<string>> userRoles = new List<IdentityUserRole<string>>();
// Add Password For All Users
users[0].PasswordHash = passwordHasher.HashPassword(users[0], "User.123");
users[1].PasswordHash = passwordHasher.HashPassword(users[1], "User.155");
userRoles.Add(new IdentityUserRole<string> { UserId = users[0].Id, RoleId =
roles.First(q => q.Name == "User").Id });
userRoles.Add(new IdentityUserRole<string> { UserId = users[1].Id, RoleId =
roles.First(q => q.Name == "Admin").Id });
builder.Entity<IdentityUserRole<string>>().HasData(userRoles);
}}
in DBContext
public class AppDbContext : IdentityDbContext<ApplicationUser>
{
public AppDbContext(DbContextOptions<AppDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
// Use seed method here
builder.Seed();
base.OnModelCreating(builder);
}}
Add the following class in Models namespace. It works for adding multiple users and roles, and will also add roles to existing users (e.g. facbook logins). Call it like this app.SeedUsersAndRoles(); from startup.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.AspNet.Identity;
namespace MyApplication.Models
{
public static class DataSeeder
{
public static async void SeedUsersAndRoles(this IApplicationBuilder app)
{
var context = app.ApplicationServices.GetService<ApplicationDbContext>();
UserWithRoles[] usersWithRoles = {
new UserWithRoles("Admin", new string[] { "Administrator" , "Distributor" },"somepassword"),//user and optional roles and password you want to seed
new UserWithRoles("PlainUser"),
new UserWithRoles("Jojo",new string[]{"Distributor" }) //seed roles to existing users (e.g. facebook login).
};
foreach (var userWithRoles in usersWithRoles)
{
foreach (string role in userWithRoles.Roles)
if (!context.Roles.Any(r => r.Name == role))
{
var roleStore = new RoleStore<IdentityRole>(context);
await roleStore.CreateAsync(new IdentityRole(role));
}
var ExistingUser = context.Users.FirstOrDefault(p => p.NormalizedUserName == userWithRoles.User.NormalizedUserName);
if (ExistingUser == null) //the following syntax: !context.Users.FirstOrDefault(p => p.NormalizedUserName == userWithRoles.User.NormalizedUserName))
//provokes execption:(ExecuteReader requires an open and available Connection.)
await new UserStore<ApplicationUser>(context).CreateAsync(userWithRoles.User);
await app.AssignRoles(userWithRoles); //assign also to existing users.
}
context.SaveChangesAsync();
}
public static async Task<IdentityResult> AssignRoles(this IApplicationBuilder app, UserWithRoles uWR)
{
UserManager<ApplicationUser> _userManager = app.ApplicationServices.GetService<UserManager<ApplicationUser>>();
ApplicationUser user = await _userManager.FindByNameAsync(uWR.User.NormalizedUserName);
var result = await _userManager.AddToRolesAsync(user, uWR.Roles);
return result;
}
}
public class UserWithRoles
{
private ApplicationUser user;
public ApplicationUser User { get { return user; } }
public string[] Roles { get; set; }
public UserWithRoles(string name, string[] roles = null, string password = "secret")
{
if (roles != null)
Roles = roles;
else
Roles = new string[] { };
user = new ApplicationUser
{
Email = name + "#gmail.com", NormalizedEmail = name.ToUpper() + "#GMAIL.COM",
UserName = name, NormalizedUserName = name.ToUpper(),
PhoneNumber = "+1312341234",
EmailConfirmed = true,
PhoneNumberConfirmed = true,
SecurityStamp = Guid.NewGuid().ToString("D"),
};
user.PasswordHash = new PasswordHasher<ApplicationUser>().HashPassword(user, password);
}
}
}
So this is solution based on Muhammad Abdullah answer. Included few code improvements, improved readability of code and got it to work with .net core 2.
public class Seed
{
public static async Task Initialize(IServiceProvider serviceProvider, IConfiguration configuration)
{
var usrName = configuration.GetSection("Admin").GetSection("UserName").Value;
var email = configuration.GetSection("Admin").GetSection("Email").Value;
var pass = configuration.GetSection("Admin").GetSection("Pass").Value;
var roles = new string[4] { OWNER, ADMIN, SENIOR, USER };
if(await CreateUser(serviceProvider, email, usrName, pass, roles))
{
await AddToRoles(serviceProvider, email, roles);
}
}
private static async Task<bool> CreateUser(IServiceProvider serviceProvider, string email, string usrName, string pass, string[] roles)
{
var res = false;
using (var scope = serviceProvider.CreateScope())
{
var context = scope.ServiceProvider.GetService<BaseContext>();
if (!context.ApplicationUsers.Any(u => u.NormalizedUserName == usrName.ToUpper()))
{
var roleStore = scope.ServiceProvider.GetService<RoleManager<IdentityRole>>();
foreach (string role in roles)
{
if (!context.Roles.Any(r => r.Name == role))
{
await roleStore.CreateAsync(new IdentityRole(role)).ConfigureAwait(false);
}
}
var user = new ApplicationUser
{
UserName = usrName,
Email = email,
EmailConfirmed = true,
NormalizedEmail = email.ToUpper(),
NormalizedUserName = usrName.ToUpper(),
PhoneNumber = null,
PhoneNumberConfirmed = true,
SecurityStamp = Guid.NewGuid().ToString()
};
var password = new PasswordHasher<ApplicationUser>();
user.PasswordHash = password.HashPassword(user, pass); ;
var userStore = new UserStore<ApplicationUser>(context);
res = (await userStore.CreateAsync(user).ConfigureAwait(false)).Succeeded;
}
return res;
}
}
private static async Task AddToRoles(IServiceProvider serviceProvider, string email, string[] roles)
{
using (var scope = serviceProvider.CreateScope())
{
var userManager = scope.ServiceProvider.GetService<UserManager<ApplicationUser>>();
var usr = await userManager.FindByEmailAsync(email).ConfigureAwait(false);
await userManager.AddToRolesAsync(usr, roles).ConfigureAwait(false);
}
}
}
Seems this thread is very old, but it will still work for someone who wants to seed their identity tables data in entityframework core.
You can simple try the below.
modelBuilder.Entity<IdentityUser>().HasData(
new IdentityUser { Id= "-1", UserName="sagark",PasswordHash="sagark", Email="emailid goes here" }
);
The following line create the entry in the AspNetRoles table but does not populate the NormalizedName column.
Substitute with the following for this column to be populated:
RoleManager<IdentityRole> roleManager = serviceProvider.GetService<RoleManager<IdentityRole>>();
roleManager.CreateAsync(new IdentityRole(role));

Resources