c# – Asp.Net Identity, how to use only Roles or Claims?

Question:

Returning to the same subject: ASP.NET Identity with EntityFramework .

Regardless of the size or planning of my project, if I need to use only the Roles part or only the Claims part, is this possible? And mainly, how?

Finally, inhibiting properties and methods related to one or the other and not creating database tables related to what was disabled.

Answer:

Answering the heart of the matter, yes. It's possible.

Using Roles

The default ASP.NET MVC5 project configured with ASP.NET Identity does not have a Role provider implementation already configured, but this is quite simple to configure:

Step 1: Create an ApplicationRoleManager

ApplicationRoleManager is a Singleton that configures RoleManager in your application. RoleManager is the ASP.NET Identity implementation for the Roles schema, very similar to ASP.NET Membership's RoleProvider .

There are several ways to do this. What I did is implementing the class inside App_Start/IdentityConfig.cs :

public class ApplicationRoleManager : RoleManager<IdentityRole, string>
{
    public ApplicationRoleManager(IRoleStore<IdentityRole, string> roleStore)
        : base(roleStore)
    {
    }

    public static ApplicationRoleManager Create(IdentityFactoryOptions<ApplicationRoleManager> options, IOwinContext context)
    {
        return new ApplicationRoleManager(new RoleStore<IdentityRole>(new ApplicationDbContext()));
    }
}

(I plan to improve this implementation someday, but it serves the answer for now).

Step 2: Create a CRUD for Roles

Controllers/RolesController.cs

namespace SeuProjeto.Controllers
{
    public class RolesController : Controller
    {
        private ApplicationDbContext db = new ApplicationDbContext();
        private readonly ApplicationRoleManager _roleManager = new ApplicationRoleManager(new RoleStore<IdentityRole>(db));

        // GET: Roles
        public ActionResult Index()
        {
            var roles = _roleManager.Roles.ToList();
            return View(roles);
        }

        public ActionResult Create()
        {
            return View();
        }

        [HttpPost]
        public async Task<ActionResult> Create(IdentityRole role)
        {
            if (ModelState.IsValid)
            {
                await _roleManager.CreateAsync(role);
                return RedirectToAction("Index");
            }

            return View(role);
        }

        public ActionResult Edit(Guid id)
        {
            var role = _roleManager.Roles.SingleOrDefault(r => r.Id == id.ToString());
            return View(role);
        }

        [HttpPost]
        public async Task<ActionResult> Edit(IdentityRole role)
        {
            if (ModelState.IsValid)
            {
                await _roleManager.UpdateAsync(role);
                return RedirectToAction("Index");
            }

            return View(role);
        }

        // GET: Roles/Delete/5
        public ActionResult Delete(Guid? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            var role = _roleManager.Roles.SingleOrDefault(r => r.Id == id.ToString());
            if (role == null)
            {
                return HttpNotFound();
            }
            return View(role);
        }

        // POST: Roles/Delete/5
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> DeleteConfirmed(Guid id)
        {
            var role = await _roleManager.Roles.SingleOrDefaultAsync(r => r.Id == id.ToString());
            _roleManager.Delete(role);
            return RedirectToAction("Index");
        }
    }
}

Views/Index.cshtml

@model IEnumerable<Microsoft.AspNet.Identity.EntityFramework.IdentityRole>

<table class="table">
    <thead>
        <tr>
            <th>Id</th>
            <th>Name</th>
            <th>Users</th>
        </tr>
    </thead>
    <tbody>
        @foreach (var role in Model)
        {
            <tr>
                <td>@role.Id</td>
                <td>@role.Name</td>
                <td>
                    @role.Users.Count
                </td>
            </tr>
        }
    </tbody>
</table>

Views/Create.cshtml

@model Microsoft.AspNet.Identity.EntityFramework.IdentityRole

@using (Html.BeginForm())
{
    @Html.LabelFor(model => model.Name)
    @Html.EditorFor(model => model.Name)

    <button type="submit">Enviar</button>
}

Views/Edit.cshtml

@model Microsoft.AspNet.Identity.EntityFramework.IdentityRole

@using (Html.BeginForm())
{
    @Html.HiddenFor(model => model.Id)
    @Html.LabelFor(model => model.Name)
        @Html.EditorFor(model => model.Name)

    <button type="submit">Enviar</button>
}

Views/Delete.cshtml

@model Microsoft.AspNet.Identity.EntityFramework.IdentityRole

<h3>Are you sure you want to delete this?</h3>
<div>
    <h4>Color</h4>
    <hr />
    <dl class="dl-horizontal">
        <dt>
            @Html.DisplayNameFor(model => model.Name)
        </dt>

        <dd>
            @Html.DisplayFor(model => model.Name)
        </dd>
    </dl>

    @using (Html.BeginForm())
    {
        @Html.AntiForgeryToken()

        <div class="form-actions no-color">
            <input type="submit" value="Delete" class="btn btn-default" /> |
            @Html.ActionLink("Back to List", "Index")
        </div>
    }
</div>

To insert a user into a Role :

var userManager = HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
var currentUser = userManager.FindByName(User.Identity.GetUserName());

var roleresult = userManager.AddToRole(currentUser.Id, "Nome da Role");

To check if a user is in a Role , the command is the same as for ASP.NET Membership:

if (User.IsInRole("Padrão")) { ... }

The command can be used in Razor, in any View

Using Claims

I don't know if it's worth describing the whole process because Claims is based on the concept of validation policies. It would be necessary to define a validation policy before I could draft an answer (and which would therefore merit a separate question).

In any case, here is an example of a Claims implementation using the zip code of the user's address as a policy .

Addendum: Why it is not possible to change the structure of tables

Inside your project's Models\IdentityModels.cs , we have the following class:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser> { ... }

IdentityDbContext has the following implementation in its source code:

    /// <summary>
    ///     Maps table names, and sets up relationships between the various user entities
    /// </summary>
    /// <param name="modelBuilder"></param>
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        if (modelBuilder == null)
        {
            throw new ArgumentNullException("modelBuilder");
        }

        // Needed to ensure subclasses share the same table
        var user = modelBuilder.Entity<TUser>()
            .ToTable("AspNetUsers");
        user.HasMany(u => u.Roles).WithRequired().HasForeignKey(ur => ur.UserId);
        user.HasMany(u => u.Claims).WithRequired().HasForeignKey(uc => uc.UserId);
        user.HasMany(u => u.Logins).WithRequired().HasForeignKey(ul => ul.UserId);
        user.Property(u => u.UserName)
            .IsRequired()
            .HasMaxLength(256)
            .HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute("UserNameIndex") {IsUnique = true}));

        // CONSIDER: u.Email is Required if set on options?
        user.Property(u => u.Email).HasMaxLength(256);

        modelBuilder.Entity<TUserRole>()
            .HasKey(r => new {r.UserId, r.RoleId})
            .ToTable("AspNetUserRoles");

        modelBuilder.Entity<TUserLogin>()
            .HasKey(l => new {l.LoginProvider, l.ProviderKey, l.UserId})
            .ToTable("AspNetUserLogins");

        modelBuilder.Entity<TUserClaim>()
            .ToTable("AspNetUserClaims");

        var role = modelBuilder.Entity<TRole>()
            .ToTable("AspNetRoles");
        role.Property(r => r.Name)
            .IsRequired()
            .HasMaxLength(256)
            .HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute("RoleNameIndex") {IsUnique = true}));
        role.HasMany(r => r.Users).WithRequired().HasForeignKey(ur => ur.RoleId);
    }

To avoid building these tables, you would have to rewrite IdentityDbContext , dropping the default. Not that it's impossible, but for that you would have to redeploy the entire package, which I don't think is reasonable for such a small desired effect.

Scroll to Top