Answered by:
Getting started with roles in ASP.NET Identity and MVC

Question
-
User-78754308 posted
Hi all,
I'm new to MVC and Identity. I used webforms and Membership for many many years and I'm finding the transition to be very difficult. There are many examples on the web, but I haven't had any luck with any of them. My last attempt was following the instructions on a site which calls for installing the IdentitySample from Nuget, which has completely hosed my entire project.
I'm just trying to have Identity with Roles - but this seems to be an impossible task.
[vent] Microsoft has really lost any semblance of consistency or leadership with ASP.NET MVC, branching off into multiple paths and leaving developers stranded with few resources in order to do our jobs. Since we switched to MVC, I seem to be wasting my time researching how to do things (making sure I don't accidentally use a MVC Core example) rather than just doing things. It's very frustrating, and I think ASP.NET has seriously lost its way. [/vent]
Anyway, if anyone could help me out I'd greatly appreciate it. It looks like I screwed up this VS Project to the point where I have to start over anyway, so maybe that'll make it easier.
Thank you very much!
-P
Wednesday, July 25, 2018 1:13 PM
Answers
-
User475983607 posted
There's a "Learn" link in the menu above. Clicking the link will get you to the security tutorials.
https://www.asp.net/mvc/overview/security
All you have to do is create a new project and select the "Individual Account" option.
Once the project is created, then you have a few decision to make. Similar to ASP Membership, ASP Identity will create a database when the first user is registered. This can be good or bad depending on your database design intentions. At this point, you might want to enable code first migrations and use the Identity DB as you main app DB too. This is the same decision you had to make using ASP Membership.
Let's assume you registered an account to create the Identity tables. There are several option for adding roles. The most straight forward is writing a SQL script since roles do not change much.
INSERT INTO [dbo].[AspNetRoles] ([Id], [Name]) VALUES(NEWID(), 'SuperUser')
ASP Identity, like ASP Membership, is an API. The default ASP Identity template fires up the UserManager and SigninManager APIs. Turn on the RoleManager API to programmatically INSERT roles in the AspNetRoles table. By the way, you had the same decisions to make with ASP Membership. Back then you had to either write a SQL script to seed roles, use the Web Site Administration Tool, or write code to add roles to the DB through an admin interface.
Create an ApplicationRoleManager class in the models folder of your project.
using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.EntityFramework; using Microsoft.AspNet.Identity.Owin; using Microsoft.Owin; namespace MvcIdentityDemo.Models { public class ApplicationRoleManager : RoleManager<IdentityRole> { public ApplicationRoleManager(IRoleStore<IdentityRole, string> store) : base(store) { } public static ApplicationRoleManager Create(IdentityFactoryOptions<ApplicationRoleManager> options, IOwinContext context) { var roleStore = new RoleStore<IdentityRole>(context.Get<ApplicationDbContext>()); return new ApplicationRoleManager(roleStore); } } }
Then add the role manager to the OWIN startup configuration. OWIN is tool that lets you plug features into an ASP.NET applications.
Open Start.Auth.cs file and add the role manager configuration.
public partial class Startup { // For more information on configuring authentication, please visit https://go.microsoft.com/fwlink/?LinkId=301864 public void ConfigureAuth(IAppBuilder app) { // Configure the db context, user manager and signin manager to use a single instance per request app.CreatePerOwinContext(ApplicationDbContext.Create); app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create); app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create); app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
Open the AccountController.cs file and add the RoleManager using the SigninManager or UserManager properties as a guide. I assume that you'll eventually add actions to the AccountController for managing roles. Or you can create a new controller for managing roles.
namespace MvcIdentityDemo.Controllers { [Authorize] public class AccountController : Controller { private ApplicationSignInManager _signInManager; private ApplicationUserManager _userManager; private ApplicationRoleManager _roleManager; public AccountController() { } public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager, ApplicationRoleManager roleManager ) { UserManager = userManager; SignInManager = signInManager; RoleManager = roleManager; } public ApplicationSignInManager SignInManager { get { return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>(); } private set { _signInManager = value; } } public ApplicationUserManager UserManager { get { return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>(); } private set { _userManager = value; } } public ApplicationRoleManager RoleManager { get { return _roleManager ?? HttpContext.GetOwinContext().Get<ApplicationRoleManager>(); } private set { _roleManager = value; } }
The following implementation adds the Admin role to the AspNetRoles table.
var result = RoleManager.Create(new Microsoft.AspNet.Identity.EntityFramework.IdentityRole("Admin"));
Since ASP Identity uses EF out of the box, you can also add roles using the DbContext and standard EF syntax.
Finally, the UserManager assigns roles to a user.
var result = UserManager.AddToRole("theUserId", "Admin");
- Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
Wednesday, July 25, 2018 2:28 PM
All replies
-
User475983607 posted
What is the problem you are trying to solve? Add roles to the AspNetRoles table? Assign a user to a role? Secure an action?
Wednesday, July 25, 2018 1:29 PM -
User475983607 posted
There's a "Learn" link in the menu above. Clicking the link will get you to the security tutorials.
https://www.asp.net/mvc/overview/security
All you have to do is create a new project and select the "Individual Account" option.
Once the project is created, then you have a few decision to make. Similar to ASP Membership, ASP Identity will create a database when the first user is registered. This can be good or bad depending on your database design intentions. At this point, you might want to enable code first migrations and use the Identity DB as you main app DB too. This is the same decision you had to make using ASP Membership.
Let's assume you registered an account to create the Identity tables. There are several option for adding roles. The most straight forward is writing a SQL script since roles do not change much.
INSERT INTO [dbo].[AspNetRoles] ([Id], [Name]) VALUES(NEWID(), 'SuperUser')
ASP Identity, like ASP Membership, is an API. The default ASP Identity template fires up the UserManager and SigninManager APIs. Turn on the RoleManager API to programmatically INSERT roles in the AspNetRoles table. By the way, you had the same decisions to make with ASP Membership. Back then you had to either write a SQL script to seed roles, use the Web Site Administration Tool, or write code to add roles to the DB through an admin interface.
Create an ApplicationRoleManager class in the models folder of your project.
using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.EntityFramework; using Microsoft.AspNet.Identity.Owin; using Microsoft.Owin; namespace MvcIdentityDemo.Models { public class ApplicationRoleManager : RoleManager<IdentityRole> { public ApplicationRoleManager(IRoleStore<IdentityRole, string> store) : base(store) { } public static ApplicationRoleManager Create(IdentityFactoryOptions<ApplicationRoleManager> options, IOwinContext context) { var roleStore = new RoleStore<IdentityRole>(context.Get<ApplicationDbContext>()); return new ApplicationRoleManager(roleStore); } } }
Then add the role manager to the OWIN startup configuration. OWIN is tool that lets you plug features into an ASP.NET applications.
Open Start.Auth.cs file and add the role manager configuration.
public partial class Startup { // For more information on configuring authentication, please visit https://go.microsoft.com/fwlink/?LinkId=301864 public void ConfigureAuth(IAppBuilder app) { // Configure the db context, user manager and signin manager to use a single instance per request app.CreatePerOwinContext(ApplicationDbContext.Create); app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create); app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create); app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
Open the AccountController.cs file and add the RoleManager using the SigninManager or UserManager properties as a guide. I assume that you'll eventually add actions to the AccountController for managing roles. Or you can create a new controller for managing roles.
namespace MvcIdentityDemo.Controllers { [Authorize] public class AccountController : Controller { private ApplicationSignInManager _signInManager; private ApplicationUserManager _userManager; private ApplicationRoleManager _roleManager; public AccountController() { } public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager, ApplicationRoleManager roleManager ) { UserManager = userManager; SignInManager = signInManager; RoleManager = roleManager; } public ApplicationSignInManager SignInManager { get { return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>(); } private set { _signInManager = value; } } public ApplicationUserManager UserManager { get { return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>(); } private set { _userManager = value; } } public ApplicationRoleManager RoleManager { get { return _roleManager ?? HttpContext.GetOwinContext().Get<ApplicationRoleManager>(); } private set { _roleManager = value; } }
The following implementation adds the Admin role to the AspNetRoles table.
var result = RoleManager.Create(new Microsoft.AspNet.Identity.EntityFramework.IdentityRole("Admin"));
Since ASP Identity uses EF out of the box, you can also add roles using the DbContext and standard EF syntax.
Finally, the UserManager assigns roles to a user.
var result = UserManager.AddToRole("theUserId", "Admin");
- Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
Wednesday, July 25, 2018 2:28 PM -
User-78754308 posted
Hi! Thank you so much for taking the time to help me - I really really appreciate it. Your explanation was very good and disarming.
I followed all your instructions, but I'm getting some errors throughout using the new code :( Here's what I'm getting:
Using the generic type 'Microsoft.AspNet.Identity.IRoleStore<TRole>' requires 1 type arguments ApplicationRoleManager.cs The type or namespace name 'ApplicationSignInManager' could not be found (are you missing a using directive or an assembly reference?) AccountController.cs The type or namespace name 'IdentityFactoryOptions' could not be found (are you missing a using directive or an assembly reference?) ApplicationRoleManager.cs The type or namespace name 'ApplicationUserManager' could not be found (are you missing a using directive or an assembly reference?) AccountController.cs The type or namespace name 'ApplicationSignInManager' could not be found (are you missing a using directive or an assembly reference?) AccountController.cs The type or namespace name 'ApplicationUserManager' could not be found (are you missing a using directive or an assembly reference?) AccountController.cs
Thank you again very much for taking the time to help! It's so so appreciated.
Wednesday, July 25, 2018 5:02 PM -
User475983607 posted
I followed all your instructions, but I'm getting some errors throughout using the new codeThe instructions should work. All you had to do was simple add a role to the AspNetRoles table. You can add the RoleManager but that should not affect the already existing SignInManager and UserManager.
I'm at a bit of a loss, the error messages are fine but not enough to figure out what you're doing. Can you post the code changes as well? Are you building an ASP.NET MVC application?
Wednesday, July 25, 2018 6:35 PM -
User-78754308 posted
Sure thing, here are the two files giving errors:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.EntityFramework; using Microsoft.AspNet.Identity.Owin; using Microsoft.Owin; namespace GeoTime2.Models { public class ApplicationRoleManager : RoleManager<IdentityRole> { public ApplicationRoleManager(IRoleStore<IdentityRole, string> store) : base(store) { } public static ApplicationRoleManager Create(IdentityFactoryOptions<ApplicationRoleManager> options, IOwinContext context) { var roleStore = new RoleStore<IdentityRole>(context.Get<ApplicationDbContext>()); return new ApplicationRoleManager(roleStore); } } }
[Using the generic type 'Microsoft.AspNet.Identity.IRoleStore<TRole>' requires 1 type arguments Line 14, col 39]
[The type or namespace name 'IdentityFactoryOptions' could not be found (are you missing a using directive or an assembly reference?) Line 18 col 53]
using System; using System.Collections.Generic; using System.Linq; using System.Security.Claims; using System.Threading.Tasks; using System.Web; using System.Web.Mvc; using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.EntityFramework; using Microsoft.Owin.Security; using GeoTime2.Models; namespace GeoTime2.Controllers { [Authorize] public class AccountController : Controller { private ApplicationSignInManager _signInManager; private ApplicationUserManager _userManager; private ApplicationRoleManager _roleManager; public AccountController() : this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()))) { } public ApplicationSignInManager SignInManager { get { return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>(); } private set { _signInManager = value; } } public ApplicationUserManager UserManager { get { return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>(); } private set { _userManager = value; } } public ApplicationRoleManager RoleManager { get { return _roleManager ?? HttpContext.GetOwinContext().Get<ApplicationRoleManager>(); } private set { _roleManager = value; } } public AccountController(UserManager<ApplicationUser> userManager) { UserManager = userManager; SignInManager = signInManager; RoleManager = roleManager; } //public UserManager<ApplicationUser> UserManager { get; private set; } // // GET: /Account/Login [AllowAnonymous] public ActionResult Login(string returnUrl) { ViewBag.ReturnUrl = returnUrl; return View(); } // // POST: /Account/Login [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task<ActionResult> Login(LoginViewModel model, string returnUrl) { if (ModelState.IsValid) { var user = await UserManager.FindAsync(model.UserName, model.Password); if (user != null) { await SignInAsync(user, model.RememberMe); return RedirectToLocal(returnUrl); } else { ModelState.AddModelError("", "Invalid username or password."); } } // If we got this far, something failed, redisplay form return View(model); } // // GET: /Account/Register [AllowAnonymous] public ActionResult Register() { return View(); } // // POST: /Account/Register [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task<ActionResult> Register(RegisterViewModel model) { if (ModelState.IsValid) { var user = new ApplicationUser() { UserName = model.UserName }; var result = await UserManager.CreateAsync(user, model.Password); if (result.Succeeded) { await SignInAsync(user, isPersistent: false); return RedirectToAction("Index", "Home"); } else { AddErrors(result); } } // If we got this far, something failed, redisplay form return View(model); } // // POST: /Account/Disassociate [HttpPost] [ValidateAntiForgeryToken] public async Task<ActionResult> Disassociate(string loginProvider, string providerKey) { ManageMessageId? message = null; IdentityResult result = await UserManager.RemoveLoginAsync(User.Identity.GetUserId(), new UserLoginInfo(loginProvider, providerKey)); if (result.Succeeded) { message = ManageMessageId.RemoveLoginSuccess; } else { message = ManageMessageId.Error; } return RedirectToAction("Manage", new { Message = message }); } // // GET: /Account/Manage public ActionResult Manage(ManageMessageId? message) { ViewBag.StatusMessage = message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed." : message == ManageMessageId.SetPasswordSuccess ? "Your password has been set." : message == ManageMessageId.RemoveLoginSuccess ? "The external login was removed." : message == ManageMessageId.Error ? "An error has occurred." : ""; ViewBag.HasLocalPassword = HasPassword(); ViewBag.ReturnUrl = Url.Action("Manage"); return View(); } // // POST: /Account/Manage [HttpPost] [ValidateAntiForgeryToken] public async Task<ActionResult> Manage(ManageUserViewModel model) { bool hasPassword = HasPassword(); ViewBag.HasLocalPassword = hasPassword; ViewBag.ReturnUrl = Url.Action("Manage"); if (hasPassword) { if (ModelState.IsValid) { IdentityResult result = await UserManager.ChangePasswordAsync(User.Identity.GetUserId(), model.OldPassword, model.NewPassword); if (result.Succeeded) { return RedirectToAction("Manage", new { Message = ManageMessageId.ChangePasswordSuccess }); } else { AddErrors(result); } } } else { // User does not have a password so remove any validation errors caused by a missing OldPassword field ModelState state = ModelState["OldPassword"]; if (state != null) { state.Errors.Clear(); } if (ModelState.IsValid) { IdentityResult result = await UserManager.AddPasswordAsync(User.Identity.GetUserId(), model.NewPassword); if (result.Succeeded) { return RedirectToAction("Manage", new { Message = ManageMessageId.SetPasswordSuccess }); } else { AddErrors(result); } } } // If we got this far, something failed, redisplay form return View(model); } // // POST: /Account/ExternalLogin [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public ActionResult ExternalLogin(string provider, string returnUrl) { // Request a redirect to the external login provider return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl })); } // // GET: /Account/ExternalLoginCallback [AllowAnonymous] public async Task<ActionResult> ExternalLoginCallback(string returnUrl) { var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(); if (loginInfo == null) { return RedirectToAction("Login"); } // Sign in the user with this external login provider if the user already has a login var user = await UserManager.FindAsync(loginInfo.Login); if (user != null) { await SignInAsync(user, isPersistent: false); return RedirectToLocal(returnUrl); } else { // If the user does not have an account, then prompt the user to create an account ViewBag.ReturnUrl = returnUrl; ViewBag.LoginProvider = loginInfo.Login.LoginProvider; return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { UserName = loginInfo.DefaultUserName }); } } // // POST: /Account/LinkLogin [HttpPost] [ValidateAntiForgeryToken] public ActionResult LinkLogin(string provider) { // Request a redirect to the external login provider to link a login for the current user return new ChallengeResult(provider, Url.Action("LinkLoginCallback", "Account"), User.Identity.GetUserId()); } // // GET: /Account/LinkLoginCallback public async Task<ActionResult> LinkLoginCallback() { var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey, User.Identity.GetUserId()); if (loginInfo == null) { return RedirectToAction("Manage", new { Message = ManageMessageId.Error }); } var result = await UserManager.AddLoginAsync(User.Identity.GetUserId(), loginInfo.Login); if (result.Succeeded) { return RedirectToAction("Manage"); } return RedirectToAction("Manage", new { Message = ManageMessageId.Error }); } // // POST: /Account/ExternalLoginConfirmation [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task<ActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl) { if (User.Identity.IsAuthenticated) { return RedirectToAction("Manage"); } if (ModelState.IsValid) { // Get the information about the user from the external login provider var info = await AuthenticationManager.GetExternalLoginInfoAsync(); if (info == null) { return View("ExternalLoginFailure"); } var user = new ApplicationUser() { UserName = model.UserName }; var result = await UserManager.CreateAsync(user); if (result.Succeeded) { result = await UserManager.AddLoginAsync(user.Id, info.Login); if (result.Succeeded) { await SignInAsync(user, isPersistent: false); return RedirectToLocal(returnUrl); } } AddErrors(result); } ViewBag.ReturnUrl = returnUrl; return View(model); } // // POST: /Account/LogOff [HttpPost] [ValidateAntiForgeryToken] public ActionResult LogOff() { AuthenticationManager.SignOut(); return RedirectToAction("Index", "Home"); } // // GET: /Account/ExternalLoginFailure [AllowAnonymous] public ActionResult ExternalLoginFailure() { return View(); } [ChildActionOnly] public ActionResult RemoveAccountList() { var linkedAccounts = UserManager.GetLogins(User.Identity.GetUserId()); ViewBag.ShowRemoveButton = HasPassword() || linkedAccounts.Count > 1; return (ActionResult)PartialView("_RemoveAccountPartial", linkedAccounts); } protected override void Dispose(bool disposing) { if (disposing && UserManager != null) { UserManager.Dispose(); UserManager = null; } base.Dispose(disposing); } #region Helpers // Used for XSRF protection when adding external logins private const string XsrfKey = "XsrfId"; private IAuthenticationManager AuthenticationManager { get { return HttpContext.GetOwinContext().Authentication; } } private async Task SignInAsync(ApplicationUser user, bool isPersistent) { AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie); var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie); AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity); } private void AddErrors(IdentityResult result) { foreach (var error in result.Errors) { ModelState.AddModelError("", error); } } private bool HasPassword() { var user = UserManager.FindById(User.Identity.GetUserId()); if (user != null) { return user.PasswordHash != null; } return false; } public enum ManageMessageId { ChangePasswordSuccess, SetPasswordSuccess, RemoveLoginSuccess, Error } private ActionResult RedirectToLocal(string returnUrl) { if (Url.IsLocalUrl(returnUrl)) { return Redirect(returnUrl); } else { return RedirectToAction("Index", "Home"); } } private class ChallengeResult : HttpUnauthorizedResult { public ChallengeResult(string provider, string redirectUri) : this(provider, redirectUri, null) { } public ChallengeResult(string provider, string redirectUri, string userId) { LoginProvider = provider; RedirectUri = redirectUri; UserId = userId; } public string LoginProvider { get; set; } public string RedirectUri { get; set; } public string UserId { get; set; } public override void ExecuteResult(ControllerContext context) { var properties = new AuthenticationProperties() { RedirectUri = RedirectUri }; if (UserId != null) { properties.Dictionary[XsrfKey] = UserId; } context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider); } } #endregion } }
[The type or namespace name 'ApplicationSignInManager' could not be found (are you missing a using directive or an assembly reference?) Line 18, col 17]
[The type or namespace name 'ApplicationUserManager' could not be found (are you missing a using directive or an assembly reference?) Line 19 col 17]
[The type or namespace name 'ApplicationSignInManager' could not be found (are you missing a using directive or an assembly reference?) Line 27 col 16]
[The type or namespace name 'ApplicationUserManager' could not be found (are you missing a using directive or an assembly reference?) Line 39 col 16]
Wednesday, July 25, 2018 7:07 PM -
User475983607 posted
The errors look look like you are missing "using" statements If you see a red squiggly under a bit a code, place you cursor over the code. Visual Studio will show a helper that should help fix the reference error.
Wednesday, July 25, 2018 7:24 PM