Simple Identity In Asp.Net Core Tutorial from scratch part 1

Asp.net core Identity used for a security-related feature in your asp.net core application.

Asp.net core identity is a membership system that adds a login functionality to our app.

It is a built-in membership system.

Uses of Asp.net core Identity

  1. Is used to create, update, read and delete user accounts
  2. Authentication and authorization
  3. Is also used for password recovery
  4. Two-factor authentication with SMS
  5. Asp.net core Identity supports external login like Facebook, Google and Microsoft

Steps for Identity In Asp.Net Core

Step 1

  • Need to add IdentityDBContext instead of DbContext means In Your application DbContext class must inherits from IdentityDbContext class.

Now our code becomes

public class OurDbContext:IdentityDbContext
    {
        public DbSet<Student> Students { get; set; }
        public OurDbContext(DbContextOptions<OurDbContext> options):base(options)
        {
                
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
          modelBuilder.Seed();
        }
    }

The identityDbContext is present in namespace of Microsoft.AspNetCore.Identity.EntityFrameworkCore;

Now Why we inherit from IdentityDbContext instead of DbContext because it has all the DbSet properties that are used for managing the Identity-related table in underlying database.

Now we can see the inner details of the IdentityDbContext class you can see

Identity in asp.net core

Our application works because it ultimately inherits from DbContext class.

Step 2:

  • Add identity services to our application. The services which are required are added to a startup.cs class file under the configureservices method.
services.AddIdentity<IdentityUser, IdentityRole>();

Now our configureservices method in a startup.cs class looks like below

public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
            services.AddTransient<IStudentRepository,StudentRepo>();
            services.AddDbContextPool<OurDbContext>(options => options.UseSqlServer(_config.GetConnectionString("StudentDBString")));

            services.AddIdentity<IdentityUser, IdentityRole>();

            services.AddScoped<IStudentRepository, SQLStudentRepository>();
        }

In this AddIdentity method first we pass IdentityUser as the first parameter so why we use this, because if you go to definition of this class you will find it inherits from IdentityUser<string> class and if you go further it will to that class you will know it uses the field like email, username and many more like below which is used to manage the properties of registered user class.

using System;

namespace Microsoft.AspNetCore.Identity
{
  /// <summary>Represents a user in the identity system</summary>
  /// <typeparam name="TKey">The type used for the primary key for the user.</typeparam>
  public class IdentityUser<TKey> where TKey : IEquatable<TKey>
  {
    /// <summary>
    /// Initializes a new instance of <see cref="T:Microsoft.AspNetCore.Identity.IdentityUser`1" />.
    /// </summary>
    public IdentityUser()
    {
    }

    /// <summary>
    /// Initializes a new instance of <see cref="T:Microsoft.AspNetCore.Identity.IdentityUser`1" />.
    /// </summary>
    /// <param name="userName">The user name.</param>
    public IdentityUser(string userName)
      : this()
    {
      this.UserName = userName;
    }

    /// <summary>Gets or sets the primary key for this user.</summary>
    [PersonalData]
    public virtual TKey Id { get; set; }

    /// <summary>Gets or sets the user name for this user.</summary>
    [ProtectedPersonalData]
    public virtual string UserName { get; set; }

    /// <summary>Gets or sets the normalized user name for this user.</summary>
    public virtual string NormalizedUserName { get; set; }

    /// <summary>Gets or sets the email address for this user.</summary>
    [ProtectedPersonalData]
    public virtual string Email { get; set; }

    /// <summary>
    /// Gets or sets the normalized email address for this user.
    /// </summary>
    public virtual string NormalizedEmail { get; set; }

    /// <summary>
    /// Gets or sets a flag indicating if a user has confirmed their email address.
    /// </summary>
    /// <value>True if the email address has been confirmed, otherwise false.</value>
    [PersonalData]
    public virtual bool EmailConfirmed { get; set; }

    /// <summary>
    /// Gets or sets a salted and hashed representation of the password for this user.
    /// </summary>
    public virtual string PasswordHash { get; set; }

    /// <summary>
    /// A random value that must change whenever a users credentials change (password changed, login removed)
    /// </summary>
    public virtual string SecurityStamp { get; set; }

    /// <summary>
    /// A random value that must change whenever a user is persisted to the store
    /// </summary>
    public virtual string ConcurrencyStamp { get; set; } = Guid.NewGuid().ToString();

    /// <summary>Gets or sets a telephone number for the user.</summary>
    [ProtectedPersonalData]
    public virtual string PhoneNumber { get; set; }

    /// <summary>
    /// Gets or sets a flag indicating if a user has confirmed their telephone address.
    /// </summary>
    /// <value>True if the telephone number has been confirmed, otherwise false.</value>
    [PersonalData]
    public virtual bool PhoneNumberConfirmed { get; set; }

    /// <summary>
    /// Gets or sets a flag indicating if two factor authentication is enabled for this user.
    /// </summary>
    /// <value>True if 2fa is enabled, otherwise false.</value>
    [PersonalData]
    public virtual bool TwoFactorEnabled { get; set; }

    /// <summary>
    /// Gets or sets the date and time, in UTC, when any user lockout ends.
    /// </summary>
    /// <remarks>A value in the past means the user is not locked out.</remarks>
    public virtual DateTimeOffset? LockoutEnd { get; set; }

    /// <summary>
    /// Gets or sets a flag indicating if the user could be locked out.
    /// </summary>
    /// <value>True if the user could be locked out, otherwise false.</value>
    public virtual bool LockoutEnabled { get; set; }

    /// <summary>
    /// Gets or sets the number of failed login attempts for the current user.
    /// </summary>
    public virtual int AccessFailedCount { get; set; }

    /// <summary>Returns the username for this user.</summary>
    public override string ToString()
    {
      return this.UserName;
    }
  }
}

So whatever properties are present in this class you have the corresponding column to underlying table.

But if you see in this class we have an only limited set of properties but in real-time scenario we might require some different fields to add these fields we can create another custom class and make sure that it is inherited from this IdentityUser class.

So in that class we can include additional properties which we required. And then remove this identityuser class from that configureservice AddIdentity method and add our new added class in that place.

  • IdentityRole: this is another built-in class provided by asp.net core identity system and it is used to manage user roles in the system.

AddEntityFrameworkStores: this is used for accessing or retrieving data from the underlying database table.

services.AddIdentity<IdentityUser, IdentityRole>()
                .AddEntityFrameworkStores<OurDbContext>();

Step 3:

Add authentication middleware

To add authentication middleware in processing pipeline you need to add UseAuthentication extension in Configure method in configure service method in the startup.cs file. And also remember to add this before MVC middleware.

Now our code looks like this below

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            DeveloperExceptionPageOptions pageOptions = new DeveloperExceptionPageOptions {SourceCodeLineCount = 10};
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage(pageOptions);
            }
            else
            {
                app.UseExceptionHandler("/Error");

                //app.UseStatusCodePagesWithReExecute("/Error/{0}");
                //  app.UseStatusCodePagesWithRedirects("/Error/{0}");
            }
            app.UseAuthentication();
            app.UseStaticFiles();
            app.UseHsts();
            app.UseMvc(routes =>
            {
                routes.MapRoute(name: "default", template: "{controller=Home}/{action=Index}/{id?}");
                //routes.MapRoute(name: "default", template: "sagar/{controller=Home}/{action=Index}/{id?}");
            });
            
        }

Step 4:

Now in this, we have added IdentityDbContext in our DB context class so we need to add migration command to use for migration.

But doing this it will cause an error,

The entity type ‘IdentityUserLogin<string>’ requires a primary key to be defined.

The root cause of error in below class

    public class OurDbContext:IdentityDbContext
    {
        public DbSet<Student> Students { get; set; }
        public OurDbContext(DbContextOptions<OurDbContext> options):base(options)
        {
                
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
          modelBuilder.Seed();
        }
    }

In this OnModelCreating method, we need to call base class OnModelCreating method why because keys are mapped here and we need to call this for that we use base keyword to access this and our code becomes.

public class OurDbContext:IdentityDbContext
    {
        public DbSet<Student> Students { get; set; }
        public OurDbContext(DbContextOptions<OurDbContext> options):base(options)
        {
                
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
          modelBuilder.Seed();
        }
    }

Now again we run Migration command which is below in Package Manager Console

Add-Migration addIdentity 

Below is the migration code snippet

After this, you need to fire another command to update the database

Update-database

Using this command we will able to add required tables in our database and after executing this command below tables were added in the database

If you see the properties present in IdentityUser class were matching with the columns in AspNetUserLogins table see below image

For Entity Framework Part 1 Visit Link

For Entity Framework Part 2 Visit Link

For Entity Framework Code First Migration Visit Link

Sagar Jaybhay, from Maharashtra, India, is currently a Senior Software Developer. He has continuously grown in the roles that he has held in the more than seven years he has been with this company. Sagar Jaybhay is an excellent team member and prides himself on his work contributions to his team and company as a whole.

Related posts