ASP.NET Core Identity & Authorization

ASP.NET Core Identity is a robust and flexible membership system that enables you to add authentication and authorization features to your web applications. It provides essential building blocks for managing user accounts, passwords, roles, claims, tokens, and more.

Purpose and When to Use

  • User Authentication: Verify user identities and control access to protected resources.
  • User Management: Handle user registration, login, logout, password reset, and profile management.
  • Role-Based Authorization: Restrict access to specific actions or features based on user roles.
  • Claims-Based Authorization: Make authorization decisions based on claims (attributes) associated with a user.
  • External Login Providers: Integrate with external authentication providers (Google, Facebook, etc.).
  • Two-Factor Authentication (2FA): Add an extra layer of security to your login process.

Key Concepts

  • Identity Models:
    • ApplicationUser: Extends the IdentityUser class to represent your application’s users. It can include additional properties like PersonName.
    • ApplicationRole: Extends the IdentityRole class to define roles within your application.
  • UserManager<TUser>: A core service for managing user accounts (creating, deleting, finding, updating).
  • SignInManager<TUser>: Handles user sign-in and sign-out operations.
  • RoleManager<TRole>: Manages roles and their assignments to users.

Detailed Code Explanation

AccountController

  • Constructor: Injects the UserManager, SignInManager, and RoleManager services.
  • Register (GET): Displays the registration form.
  • Register (POST):
    1. Model Validation: Checks if the submitted RegisterDTO is valid.
    2. User Creation: Creates an ApplicationUser based on the RegisterDTO data.
    3. Role Handling: Creates the Admin or User role if it doesn’t exist, assigns the user to the selected role.
    4. Sign-In: Signs the user in upon successful registration.
    5. Redirect: Redirects to PersonsController.Index action.
    6. Error Handling: If user creation fails, adds errors to ModelState and re-renders the Register view.
  • Login (GET): Displays the login form.
  • Login (POST):
    1. Model Validation: Checks if the submitted LoginDTO is valid.
    2. Sign-In Attempt: Attempts to sign in using _signInManager.PasswordSignInAsync.
    3. Redirect (if successful): Redirects to Admin area’s Home/Index if admin, or to ReturnUrl/PersonsController.Index.
    4. Error Handling: If sign-in fails, adds error to ModelState and re-renders the Login view.
  • Logout:
    • [Authorize]: Ensures user is authenticated.
    • Signs the user out using _signInManager.SignOutAsync.
    • Redirects to PersonsController.Index.
  • IsEmailAlreadyRegistered:
    • [AllowAnonymous]: Allows anonymous access (useful for client-side validation).
    • Checks for existing user using _userManager.FindByEmailAsync.
    • Returns JSON indicating availability.

Program.cs

builder.Services.AddControllersWithViews();
builder.Services.AddIdentity<ApplicationUser, ApplicationRole>(options =>
{
    options.Password.RequiredLength = 6;
    options.Password.RequireNonAlphanumeric = false;
    // ... other password options
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders()
.AddUserStore<UserStore<ApplicationUser, ApplicationRole, ApplicationDbContext, Guid>>()
.AddRoleStore<RoleStore<ApplicationRole, ApplicationDbContext, Guid>>();
 
builder.Services.AddAuthorization(options =>
{
    options.FallbackPolicy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .Build();
    options.AddPolicy("NotAuthorized", policy =>
        policy.RequireAssertion(context => !context.User.Identity.IsAuthenticated));
});
 
builder.Services.ConfigureApplicationCookie(options =>
{
    options.LoginPath = "/Account/Login";
});

Notes

  • IdentityUser and IdentityRole: Extend these base classes for custom user and role models.
  • UserManager, SignInManager, RoleManager: Core services for managing users, sign-in/out, and roles.
  • [Authorize]: Attribute to restrict access to authenticated users.
  • [AllowAnonymous]: Attribute to allow anonymous access.
  • Authorization Policies: Use AddAuthorization to define custom policies.
  • Password Complexity: Configure password requirements in AddIdentity.
  • ReturnUrl: Used to redirect users back to their original destination after logging in.
  • Remote Validation: Allows for server-side validation of user input on the client-side using AJAX.
  • Areas: Organize large applications into logical areas.
  • HTTPS: Enable HTTPS in production for secure communication.
  • XSRF Protection: Use [ValidateAntiForgeryToken] and tag helpers to prevent CSRF attacks.

Key Points to Remember

ASP.NET Core Identity

  • Purpose: Robust membership system for user authentication and authorization.
  • Key Features:
    • User registration, login, logout
    • Password management (hashing, reset)
    • Role-based and claims-based authorization
    • External login providers (Google, Facebook, etc.)
    • Two-factor authentication (2FA)

Identity Models

  • ApplicationUser: Extends IdentityUser to represent your app’s users.
  • ApplicationRole: Extends IdentityRole to define roles in your app.

Key Services

  • UserManager<TUser>: Manages user accounts (create, delete, find, update).
  • SignInManager<TUser>: Handles user sign-in and sign-out.
  • RoleManager<TRole>: Manages roles and their assignments to users.

Security

  • Password Complexity: Configure password requirements in AddIdentity.
  • Authorization Policies: Use [Authorize] and Authorize(policyName).
  • ReturnUrl: Redirect users back to original destination after login.
  • HTTPS: Always enable HTTPS in production.
  • XSRF Protection: Use [ValidateAntiForgeryToken] and @Html.AntiForgeryToken().

Additional Concepts

  • User Roles: Assign users to roles to control access.
  • Areas: Organize large applications into logical areas.
  • Claims-Based Authorization: Make decisions based on claims associated with the user.

Code Snippets

Registering a user:

ApplicationUser user = new ApplicationUser() { /* ... */ };
IdentityResult result = await _userManager.CreateAsync(user, registerDTO.Password);

Signing in a user:

var result = await _signInManager.PasswordSignInAsync(loginDTO.Email, loginDTO.Password, isPersistent: false, lockoutOnFailure: false);

Checking user roles:

if (await _userManager.IsInRoleAsync(user, "Admin")) { /* ... */ }

Interview Tips

  • Concepts: Explain authentication vs. authorization, role-based vs. claims-based authorization.
  • Implementation: Demonstrate setting up user registration, login, and logout.
  • Security: Emphasize security best practices (HTTPS, XSRF protection, password hashing).
  • Customization: Discuss customizing Identity (adding user profile fields, custom authorization policies).