Skip to content
ASP.NET Core Identity — Authentication and Authorization Explained

ASP.NET Core Identity — Authentication and Authorization Explained

DodaTech Updated Jun 15, 2026 5 min read

ASP.NET Core Identity is a membership system that handles user registration, login, password hashing, role management, and claims-based authorization — all integrated with Entity Framework Core.

What You’ll Learn

You’ll master user registration and login, role and claims management, JWT token authentication, and OAuth provider integration (Google, Facebook). You’ll build a complete Identity setup with JWT auth.

Why Identity Matters

Authentication is the security foundation for every web application. ASP.NET Core Identity handles the hard parts — password hashing, account lockout, token validation — so you don’t have to. At DodaTech, Identity-based auth protects DodaZIP cloud sync and Durga Antivirus Pro subscription management.

Identity Learning Path

    flowchart LR
  A[Entity Framework] --> B[ASP.NET Core Identity]
  B --> C{You Are Here}
  C --> D[Blazor]
  C --> E[.NET MAUI]
  style C fill:#f90,color:#fff
  

Setting Up Identity

First, install the package and configure services:

dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
// Program.cs
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("Default")));

builder.Services.AddIdentity<IdentityUser, IdentityRole>()
    .AddEntityFrameworkStores<AppDbContext>()
    .AddDefaultTokenProviders();

builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true,
        ValidIssuer = builder.Configuration["Jwt:Issuer"],
        ValidAudience = builder.Configuration["Jwt:Audience"],
        IssuerSigningKey = new SymmetricSecurityKey(
            Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]))
    };
});

User Registration and Login

[ApiController]
[Route("api/auth")]
public class AuthController : ControllerBase
{
    private readonly UserManager<IdentityUser> _userManager;
    private readonly SignInManager<IdentityUser> _signInManager;
    private readonly IConfiguration _config;

    public AuthController(
        UserManager<IdentityUser> userManager,
        SignInManager<IdentityUser> signInManager,
        IConfiguration config)
    {
        _userManager = userManager;
        _signInManager = signInManager;
        _config = config;
    }

    [HttpPost("register")]
    public async Task<IActionResult> Register([FromBody] RegisterDto dto)
    {
        var user = new IdentityUser
        {
            UserName = dto.Email,
            Email = dto.Email
        };

        var result = await _userManager.CreateAsync(user, dto.Password);
        if (!result.Succeeded)
            return BadRequest(result.Errors);

        return Ok(new { Message = "User created successfully" });
    }

    [HttpPost("login")]
    public async Task<IActionResult> Login([FromBody] LoginDto dto)
    {
        var user = await _userManager.FindByEmailAsync(dto.Email);
        if (user == null)
            return Unauthorized("Invalid credentials");

        var result = await _signInManager.CheckPasswordSignInAsync(
            user, dto.Password, lockoutOnFailure: true);

        if (result.IsLockedOut)
            return Unauthorized("Account locked. Try again later.");
        if (!result.Succeeded)
            return Unauthorized("Invalid credentials");

        var token = await GenerateJwtToken(user);
        return Ok(new { Token = token });
    }

    private async Task<string> GenerateJwtToken(IdentityUser user)
    {
        var claims = new List<Claim>
        {
            new Claim(ClaimTypes.NameIdentifier, user.Id),
            new Claim(ClaimTypes.Email, user.Email),
            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
        };

        var roles = await _userManager.GetRolesAsync(user);
        claims.AddRange(roles.Select(r => new Claim(ClaimTypes.Role, r)));

        var key = new SymmetricSecurityKey(
            Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
        var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

        var token = new JwtSecurityToken(
            issuer: _config["Jwt:Issuer"],
            audience: _config["Jwt:Audience"],
            claims: claims,
            expires: DateTime.UtcNow.AddHours(24),
            signingCredentials: credentials
        );

        return new JwtSecurityTokenHandler().WriteToken(token);
    }
}

Roles and Claims

Roles group users for coarse authorization. Claims carry specific permissions:

// Creating roles
var roleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();
await roleManager.CreateAsync(new IdentityRole("Admin"));
await roleManager.CreateAsync(new IdentityRole("User"));

// Assigning roles
await _userManager.AddToRoleAsync(user, "Admin");

// Custom claims
await _userManager.AddClaimAsync(user, new Claim("Permission", "CanDeletePosts"));
await _userManager.AddClaimAsync(user, new Claim("Department", "Engineering"));

Protecting Endpoints

[ApiController]
[Route("api/admin")]
public class AdminController : ControllerBase
{
    [HttpGet("dashboard")]
    [Authorize(Roles = "Admin")]  // Role-based
    public IActionResult GetDashboard()
    {
        return Ok("Admin dashboard data");
    }

    [HttpPost("delete-user")]
    [Authorize(Policy = "CanDeleteUsers")]  // Policy-based
    public IActionResult DeleteUser(string userId)
    {
        return Ok("User deleted");
    }
}

// Registering policies with claims
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("CanDeleteUsers", policy =>
        policy.RequireClaim("Permission", "CanDeletePosts"));
    options.AddPolicy("EngineeringOnly", policy =>
        policy.RequireClaim("Department", "Engineering"));
});

OAuth Providers

Integrate Google, Facebook, or Microsoft authentication:

dotnet add package Microsoft.AspNetCore.Authentication.Google
builder.Services.AddAuthentication()
    .AddGoogle(options =>
    {
        options.ClientId = builder.Configuration["Google:ClientId"];
        options.ClientSecret = builder.Configuration["Google:ClientSecret"];
    })
    .AddFacebook(options =>
    {
        options.AppId = builder.Configuration["Facebook:AppId"];
        options.AppSecret = builder.Configuration["Facebook:AppSecret"];
    });

Common Mistakes Beginners Make

1. Storing Passwords in Plain Text

Identity hashes passwords with PBKDF2 by default. Never store raw passwords. Never disable password hashing.

2. Not Using HTTPS in Production

Tokens and credentials sent over HTTP are intercepted by anyone on the network. Always enforce HTTPS.

3. Missing Token Validation Parameters

Without validating ValidateLifetime, expired tokens are accepted. Without ValidateIssuer, tokens from any server are valid.

4. Locking Users Without Lockout Configuration

Default lockout locks after 5 failed attempts for 5 minutes. Customize Account.Lockout in Identity options.

5. Storing JWTs in Local Storage

Browser local storage is accessible via XSS. Store tokens in HttpOnly cookies or memory.

6. Ignoring CSRF with Cookie Auth

When using cookie authentication (not JWT), add anti-forgery tokens to forms. Blazor Server handles this automatically.

7. Not Refreshing Tokens

JWT tokens expire. Implement a refresh token endpoint that issues new tokens without re-authentication.

Practice Questions

1. What does UserManager<T> handle?

User creation, password management, email confirmation, phone management, and role assignment.

2. What is the difference between roles and claims?

Roles are broad categories (Admin, User). Claims are specific permissions (CanDeletePosts, Department=Engineering).

3. What does [Authorize(Roles = "Admin")] do?

It restricts the endpoint to users who belong to the “Admin” role. Unauthorized users get a 401 response.

4. Why use JWT over cookie authentication?

JWT works across APIs, mobile apps, and SPAs without CSRF concerns. Cookies are simpler for traditional MVC apps.

5. Challenge: Implement password reset.

Add a ForgotPassword endpoint that generates a token and emails it. Add a ResetPassword endpoint that validates the token and changes the password.

Mini Project: Secure API with Identity

Build a secure task management API:

  1. Register/login with JWT (as shown above)
  2. [Authorize] on all task endpoints
  3. Admin role can view all tasks. User role views only their tasks
  4. Add a CreatedByUserId claim on each task
  5. Add a policy: TaskOwnerPolicy — only the creator or admin can edit/delete

FAQ

Can Identity work with existing user tables?
Yes. You can customize the IdentityUser class and map it to existing tables. Use IdentityUser<TKey> with custom primary key types.
Is Identity only for Entity Framework?
No. While EF Core is the default, you can implement custom stores for Dapper, MongoDB, or any data source.
Does Identity support two-factor authentication?
Yes. Identity supports TOTP (authenticator apps), SMS, and email 2FA out of the box.

What’s Next

Congratulations on completing this Identity tutorial! Here’s where to go from here:

  • Practice daily — Implement one auth flow per day
  • Build a project — Secure a full-stack app with Identity
  • Explore related topics — OAuth 2.0 flows, OpenID Connect
  • Join the community — Share your auth implementations and get feedback

Remember: every expert was once a beginner. Keep securing!

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro