Skip to main content

Authentication & Authorization

Short Introduction

  • Authentication and Authorization are fundamental security concepts in .NET Core applications.
  • Authentication verifies user identity, "who you are"
  • Authorization determines what authenticated users can access, "what you can do.".
  • ASP.NET Core provides comprehensive built-in support for both.

Official Definition

  • Authentication is the process of determining whether someone or something is who or what it declares to be.
  • Authorization is the process of giving someone permission to do or have something.

Usage

# Install required packages
dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
// Program.cs
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

// Add Entity Framework
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

// Add Identity
builder.Services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
options.Password.RequireDigit = true;
options.Password.RequiredLength = 8;
options.Password.RequireNonAlphanumeric = false;
options.User.RequireUniqueEmail = true;
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();

// Add Authentication
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"]))
};
});

// Add Authorization
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AdminOnly", policy => policy.RequireRole("Admin"));
options.AddPolicy("MinimumAge", policy =>
policy.RequireClaim("age", "18", "19", "20")); // 18+
});

var app = builder.Build();

app.UseAuthentication();
app.UseAuthorization();
// Program.cs - Configure authentication
builder.Services.AddAuthentication(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"]!))
};
});

builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AdminOnly", policy =>
policy.RequireRole("Admin"));
options.AddPolicy("ManagerOrAdmin", policy =>
policy.RequireRole("Manager", "Admin"));
});

app.UseAuthentication();
app.UseAuthorization();

Authentication Schemes

  • Cookie Authentication: Traditional web applications
  • JWT Bearer: APIs and SPAs
  • Identity Server: Enterprise SSO
  • OAuth 2.0: Third-party providers

Use Cases

  • User login/logout systems
  • API security with JWT tokens
  • Role-based access control (RBAC)
  • Claims-based authorization
  • Multi-factor authentication
  • Social login integration
  • Enterprise single sign-on (SSO)

When to Use vs When Not to Use

Use When:

  • Application requires user accounts
  • Different user privilege levels needed
  • Protecting sensitive data/operations
  • Compliance requirements (GDPR, HIPAA)
  • API security is required

Don't Use When:

  • Simple public applications
  • No user-specific functionality
  • Performance is absolutely critical
  • Very basic internal tools
  • Proof-of-concept applications

Market Alternatives & Adoption

Alternatives:

  • Auth0 (SaaS identity platform)
  • Azure Active Directory B2C
  • Firebase Authentication
  • AWS Cognito
  • Okta
  • Custom JWT implementation

Market Position:

.NET Identity is the de-facto standard for .NET applications with extensive enterprise adoption.

Pros and Cons

Pros:

  • Comprehensive built-in solution
  • Highly configurable
  • Extensive customization options
  • Strong security defaults
  • Good performance
  • Integrates well with EF Core

Cons:

  • Can be complex for simple scenarios
  • Steep learning curve for advanced features
  • Database overhead for simple apps
  • UI scaffolding is basic
  • Migration complexity

Sample Usage

// JWT service
public class JwtService : IJwtService
{
private readonly IConfiguration _configuration;

public JwtService(IConfiguration configuration)
{
_configuration = configuration;
}

public string GenerateToken(User user)
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_configuration["Jwt:Key"]!);

var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.Name, user.Username),
new Claim(ClaimTypes.Email, user.Email),
new Claim(ClaimTypes.Role, user.Role)
}),
Expires = DateTime.UtcNow.AddHours(1),
SigningCredentials = new SigningCredentials(
new SymmetricSecurityKey(key),
SecurityAlgorithms.HmacSha256Signature)
};

var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
}

// Protected controller
[ApiController]
[Route("api/[controller]")]
[Authorize]
public class UsersController : ControllerBase
{
[HttpGet]
[Authorize(Roles = "Admin")]
public async Task<ActionResult<IEnumerable<User>>> GetUsers()
{
// Only admins can access this endpoint
return Ok(await _userService.GetAllUsersAsync());
}

[HttpGet("me")]
public async Task<ActionResult<User>> GetCurrentUser()
{
var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
var user = await _userService.GetUserByIdAsync(int.Parse(userId!));
return Ok(user);
}
}
// Models/ApplicationUser.cs
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime DateCreated { get; set; }
public bool IsActive { get; set; }
}

// Data/ApplicationDbContext.cs
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}

protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);

// Seed roles
builder.Entity<IdentityRole>().HasData(
new IdentityRole { Id = "1", Name = "Admin", NormalizedName = "ADMIN" },
new IdentityRole { Id = "2", Name = "User", NormalizedName = "USER" }
);
}
}

// DTOs/AuthDto.cs
public class LoginDto
{
[Required]
[EmailAddress]
public string Email { get; set; }

[Required]
public string Password { get; set; }
}

public class RegisterDto
{
[Required]
[EmailAddress]
public string Email { get; set; }

[Required]
[StringLength(100, MinimumLength = 8)]
public string Password { get; set; }

[Required]
public string FirstName { get; set; }

[Required]
public string LastName { get; set; }
}

public class AuthResponseDto
{
public string Token { get; set; }
public DateTime Expiration { get; set; }
public string UserId { get; set; }
public string Email { get; set; }
public List<string> Roles { get; set; }
}

// Services/IAuthService.cs
public interface IAuthService
{
Task<AuthResponseDto> LoginAsync(LoginDto loginDto);
Task<AuthResponseDto> RegisterAsync(RegisterDto registerDto);
Task<bool> AssignRoleAsync(string userId, string role);
string GenerateJwtToken(ApplicationUser user, IList<string> roles);
}

// Services/AuthService.cs
public class AuthService : IAuthService
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly IConfiguration _configuration;

public AuthService(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
IConfiguration configuration)
{
_userManager = userManager;
_signInManager = signInManager;
_configuration = configuration;
}

public async Task<AuthResponseDto> LoginAsync(LoginDto loginDto)
{
var user = await _userManager.FindByEmailAsync(loginDto.Email);
if (user == null)
throw new UnauthorizedAccessException("Invalid credentials");

var result = await _signInManager.CheckPasswordSignInAsync(user, loginDto.Password, false);
if (!result.Succeeded)
throw new UnauthorizedAccessException("Invalid credentials");

var roles = await _userManager.GetRolesAsync(user);
var token = GenerateJwtToken(user, roles);

return new AuthResponseDto
{
Token = token,
Expiration = DateTime.Now.AddHours(1),
UserId = user.Id,
Email = user.Email,
Roles = roles.ToList()
};
}

public async Task<AuthResponseDto> RegisterAsync(RegisterDto registerDto)
{
var user = new ApplicationUser
{
UserName = registerDto.Email,
Email = registerDto.Email,
FirstName = registerDto.FirstName,
LastName = registerDto.LastName,
DateCreated = DateTime.UtcNow,
IsActive = true
};

var result = await _userManager.CreateAsync(user, registerDto.Password);
if (!result.Succeeded)
throw new InvalidOperationException(string.Join(", ", result.Errors.Select(e => e.Description)));

await _userManager.AddToRoleAsync(user, "User");

var roles = await _userManager.GetRolesAsync(user);
var token = GenerateJwtToken(user, roles);

return new AuthResponseDto
{
Token = token,
Expiration = DateTime.Now.AddHours(1),
UserId = user.Id,
Email = user.Email,
Roles = roles.ToList()
};
}

public async Task<bool> AssignRoleAsync(string userId, string role)
{
var user = await _userManager.FindByIdAsync(userId);
if (user == null) return false;

var result = await _userManager.AddToRoleAsync(user, role);
return result.Succeeded;
}

public string GenerateJwtToken(ApplicationUser user, IList<string> roles)
{
var claims = new List<Claim>
{
new Claim(JwtRegisteredClaimNames.Sub, user.Id),
new Claim(JwtRegisteredClaimNames.Email, user.Email),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim("firstName", user.FirstName),
new Claim("lastName", user.LastName)
};

foreach (var role in roles)
{
claims.Add(new Claim(ClaimTypes.Role, role));
}

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

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

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

// Controllers/AuthController.cs
[ApiController]
[Route("api/[controller]")]
public class AuthController : ControllerBase
{
private readonly IAuthService _authService;

public AuthController(IAuthService authService)
{
_authService = authService;
}

[HttpPost("login")]
public async Task<ActionResult<AuthResponseDto>> Login(LoginDto loginDto)
{
try
{
var response = await _authService.LoginAsync(loginDto);
return Ok(response);
}
catch (UnauthorizedAccessException ex)
{
return Unauthorized(new { message = ex.Message });
}
}

[HttpPost("register")]
public async Task<ActionResult<AuthResponseDto>> Register(RegisterDto registerDto)
{
try
{
var response = await _authService.RegisterAsync(registerDto);
return Ok(response);
}
catch (InvalidOperationException ex)
{
return BadRequest(new { message = ex.Message });
}
}

[HttpGet("profile")]
[Authorize]
public async Task<ActionResult> GetProfile()
{
var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
var email = User.FindFirst(ClaimTypes.Email)?.Value;
var roles = User.FindAll(ClaimTypes.Role).Select(c => c.Value).ToList();

return Ok(new { userId, email, roles });
}

[HttpPost("assign-role")]
[Authorize(Roles = "Admin")]
public async Task<ActionResult> AssignRole([FromBody] AssignRoleDto dto)
{
var success = await _authService.AssignRoleAsync(dto.UserId, dto.Role);
if (!success)
return BadRequest("Failed to assign role");

return Ok("Role assigned successfully");
}
}

// Protected Controller Example
[ApiController]
[Route("api/[controller]")]
[Authorize] // Requires authentication
public class SecureController : ControllerBase
{
[HttpGet("admin-only")]
[Authorize(Roles = "Admin")] // Requires Admin role
public ActionResult AdminOnly()
{
return Ok("This is admin-only content");
}

[HttpGet("policy-based")]
[Authorize(Policy = "MinimumAge")] // Requires custom policy
public ActionResult PolicyBased()
{
return Ok("This requires minimum age policy");
}
}