Security in C# — Complete Guide
In this tutorial, you will learn about Security in C#. We cover key concepts, practical examples, and best practices to help you master this topic.
Hook
Security is not optional -- it is a fundamental requirement for every application. C# and .NET provide comprehensive libraries for authentication, authorization, encryption, and data protection. Understanding these security primitives helps you build applications that protect user data and resist attacks.
Learning Path
graph LR A[Security] --> B[Authentication] A --> C[Authorization] B --> D[JWT Tokens] B --> E[Identity Framework] C --> F[Policies] style A fill:#4a90d9,color:#fff style B fill:#4a90d9,color:#fff style C fill:#4a90d9,color:#fff style D fill:#4a90d9,color:#fff style E fill:#4a90d9,color:#fff style F fill:#4a90d9,color:#fff
Authentication Vs Authorization
Authentication verifies identity; authorization controls access.
// Authentication: Who are you?
// Authorization: What can you do?
[ApiController]
[Route("api/[controller]")]
public class SecureController : ControllerBase
{
[HttpGet("public")]
public IActionResult Public() => Ok("Anyone can access this");
[HttpGet("authenticated")]
[Authorize]
public IActionResult Authenticated() =>
Ok($"Hello {User.Identity?.Name}");
[HttpGet("admin-only")]
[Authorize(Roles = "Admin")]
public IActionResult AdminOnly() =>
Ok("Only admins can see this");
[HttpGet("policy")]
[Authorize(Policy = "Over21")]
public IActionResult PolicyBased() =>
Ok("Age verified access");
}
JWT Authentication
JSON Web Tokens are the standard for stateless authentication.
// Program.cs
var builder = WebApplication.CreateBuilder(args);
var jwtSettings = builder.Configuration.GetSection("Jwt");
var key = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(jwtSettings["Key"]!));
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = jwtSettings["Issuer"],
ValidAudience = jwtSettings["Audience"],
IssuerSigningKey = key,
ClockSkew = TimeSpan.Zero
};
});
builder.Services.AddAuthorization();
builder.Services.AddControllers();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
// Token generation service
public class TokenService
{
private readonly IConfiguration _config;
public TokenService(IConfiguration config) => _config = config;
public string GenerateToken(string username, string[] roles)
{
var key = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(_config["Jwt:Key"]!));
var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var claims = new List<Claim>
{
new(ClaimTypes.Name, username),
new(ClaimTypes.NameIdentifier, Guid.NewGuid().ToString()),
new("iat", DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString())
};
claims.AddRange(roles.Select(r => new Claim(ClaimTypes.Role, r)));
var token = new JwtSecurityToken(
issuer: _config["Jwt:Issuer"],
audience: _config["Jwt:Audience"],
claims: claims,
expires: DateTime.UtcNow.AddHours(1),
signingCredentials: credentials
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
}
ASP.NET Core Identity
Identity provides full user management with EF Core.
// Install: dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore
public class AppUser : IdentityUser
{
public string? DisplayName { get; set; }
public DateTime RegisteredAt { get; set; }
}
public class AppDbContext : IdentityDbContext<AppUser>
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
}
// Program.cs
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(connectionString));
builder.Services.AddIdentity<AppUser, IdentityRole>(options =>
{
// Password settings
options.Password.RequireDigit = true;
options.Password.RequiredLength = 8;
options.Password.RequireNonAlphanumeric = true;
// Lockout settings
options.Lockout.MaxFailedAccessAttempts = 5;
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(15);
// User settings
options.User.RequireUniqueEmail = true;
})
.AddEntityFrameworkStores<AppDbContext>()
.AddDefaultTokenProviders();
Authorization Policies
Create reusable authorization policies.
// In Program.cs
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("Over21", policy =>
policy.Requirements.Add(new MinimumAgeRequirement(21)));
options.AddPolicy("EmployeeOnly", policy =>
policy.RequireClaim("EmployeeId"));
options.AddPolicy("AtLeastManager", policy =>
policy.RequireRole("Admin", "Manager"));
});
// Custom requirement
public class MinimumAgeRequirement : IAuthorizationRequirement
{
public int MinimumAge { get; }
public MinimumAgeRequirement(int minimumAge) => MinimumAge = minimumAge;
}
public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
MinimumAgeRequirement requirement)
{
var ageClaim = context.User.FindFirst(c =>
c.Type == "Age" && int.TryParse(c.Value, out int age));
if (ageClaim != null && int.Parse(ageClaim.Value) >= requirement.MinimumAge)
context.Succeed(requirement);
return Task.CompletedTask;
}
}
// Register handler
builder.Services.AddScoped<IAuthorizationHandler, MinimumAgeHandler>();
Data Protection
Protect sensitive data with the data protection API.
// Program.cs
builder.Services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo("/keys"))
.ProtectKeysWithDpapi()
.SetApplicationName("MyApp");
// In a service
public class DataProtectionService
{
private readonly IDataProtector _protector;
public DataProtectionService(IDataProtectionProvider provider)
{
_protector = provider.CreateProtector("Payment.Encryption.v1");
}
public string Encrypt(string plaintext) => _protector.Protect(plaintext);
public string Decrypt(string ciphertext) => _protector.Unprotect(ciphertext);
}
Common Security Vulnerabilities
// SQL Injection (BAD)
var cmd = $"SELECT * FROM Users WHERE Name = '{input}'";
// SQL Injection (GOOD)
var cmd = "SELECT * FROM Users WHERE Name = @name";
cmd.Parameters.AddWithValue("@name", input);
// XSS (BAD)
ViewBag.Message = userInput;
// XSS (GOOD)
ViewBag.Message = HtmlEncoder.Default.Encode(userInput);
// CSRF
// ASP.NET Core includes anti-forgery tokens by default
// Use [AutoValidateAntiforgeryToken] attribute
// Open Redirect
// Validate redirect URLs against an allowlist
if (!allowedUrls.Contains(returnUrl))
returnUrl = "/";
Common Mistakes
Storing passwords in plaintext: Always hash passwords with BCrypt, Argon2, or ASP.NET Core Identity.
Not validating JWT tokens: Always validate issuer, audience, lifetime, and signing key on every request.
Over-sharing error details: Never expose stack traces or internal error details to clients. Return generic error messages.
Missing HTTPS: Always enforce HTTPS in production. Use HSTS headers to prevent protocol downgrade attacks.
Ignoring Rate Limiting: Protect APIs from brute force attacks with rate limiting middleware (.NET 7+).
Practice Questions
Implement a registration and login endpoint with ASP.NET Core Identity.
Create a custom authorization policy that checks if the user belongs to a specific department claim.
Write a middleware that validates API keys for a machine-to-machine endpoint.
Challenge: Build a multi-factor authentication flow using TOTP (Time-based One-Time Password).
FAQ
Mini Project: JWT Authentication API
Build a complete authentication API with JWT tokens.
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
[ApiController]
[Route("api/auth")]
public class AuthController : ControllerBase
{
private readonly IConfiguration _config;
public AuthController(IConfiguration config) => _config = config;
[HttpPost("login")]
public IActionResult Login([FromBody] LoginRequest request)
{
// In production: verify against Identity or user store
if (request.Username != "admin" || request.Password != "password")
return Unauthorized(new { error = "Invalid credentials" });
var token = GenerateJwt(request.Username, new[] { "User", "Admin" });
return Ok(new
{
token,
expiresAt = DateTime.UtcNow.AddHours(1)
});
}
[HttpGet("profile")]
[Authorize]
public IActionResult Profile()
{
return Ok(new
{
Username = User.Identity?.Name,
Roles = User.FindAll(ClaimTypes.Role).Select(c => c.Value),
Claims = User.Claims.Select(c => new { c.Type, c.Value })
});
}
private string GenerateJwt(string username, string[] roles)
{
var key = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(_config["Jwt:Key"]!));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var claims = new List<Claim>
{
new(ClaimTypes.Name, username),
new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
};
claims.AddRange(roles.Select(r => new Claim(ClaimTypes.Role, r)));
var token = new JwtSecurityToken(
issuer: _config["Jwt:Issuer"],
audience: _config["Jwt:Audience"],
claims: claims,
expires: DateTime.UtcNow.AddHours(1),
signingCredentials: creds);
return new JwtSecurityTokenHandler().WriteToken(token);
}
}
public record LoginRequest(string Username, string Password);
API Test:
POST /api/auth/login {"username":"admin","password":"password"}
Response: {"token":"eyJhbGci...", "expiresAt":"2026-06-28T..."}
GET /api/auth/profile
Authorization: Bearer eyJhbGci...
Response: {"username":"admin","roles":["User","Admin"]}
Security is a critical skill for every C# developer. The .NET platform provides robust, well-tested libraries for authentication, authorization, and data protection that help you build secure applications by default.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro