From Login to Locked-Down APIs (Without Losing Your Mind)
If you’ve ever built a .NET API and thought, “Anyone with this token can do everything… that’s bad, right?”
That’s exactly where JWT + Role-Based Authorization saves the day.
In this article, we’ll walk through:
What JWT authentication really does
How roles fit into the picture
A clean .NET implementation
Common mistakes (and how to avoid them)
Authentication vs Authorization (Quick Reality Check)
Before writing a single line of code:
| Concept | Meaning |
|---|
| Authentication | Who are you? |
| Authorization | What are you allowed to do? |
JWT helps us prove identity, while roles help us control access.
What Is a JWT (In Plain English)?
A JSON Web Token is a compact, self-contained token that:
Is issued after successful login
Travels with every request (usually in the Authorization header)
Contains claims like UserId, Email, and Roles
Example JWT payload:
{"sub": "42","email": "[email protected]","role": "Admin","exp": 1712345678}
No database lookup on every request.
Fast. Stateless. Scalable.
Step 1: Configure JWT Authentication in .NET
In Program.cs:
builder.Services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "MyAuthServer",
ValidAudience = "MyApi",
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes("SUPER_SECRET_KEY_123"))
};
});
builder.Services.AddAuthorization();
And don’t forget:
app.UseAuthentication();
app.UseAuthorization();
⚠️ Missing UseAuthentication() is the #1 reason JWT “doesn’t work”.
Step 2: Add Roles to Your JWT
When generating the token:
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.Email, user.Email),
new Claim(ClaimTypes.Role, user.Role) // Admin, User, Manager
};
JWT generation magic ✨ happens once — then roles travel with every request.
Step 3: Secure Endpoints with Role-Based Authorization
Now the fun part.
Single Role
[Authorize(Roles = "Admin")]
[HttpGet("admin-only")]
public IActionResult AdminOnly()
{
return Ok("Welcome, Admin!");
}
Multiple Roles
[Authorize(Roles = "Admin,Manager")]
public IActionResult ManagementArea()
{
return Ok("Managers and Admins only!");
}
Any Authenticated User
[Authorize]
public IActionResult Profile()
{
return Ok("You are logged in.");
}
Simple. Clean. Powerful.
Common Mistakes (Learn From Our Pain)
❌ Storing roles only in the database
✔️ Put roles inside the JWT claims
❌ Forgetting token expiration
✔️ Always set exp and validate lifetime
❌ Using plain text secret keys
✔️ Use environment variables or Azure Key Vault
❌ Overusing roles
✔️ For complex rules, switch to policy-based authorization
JWT + role-based authorization is:
Secure
Scalable
Perfect for modern APIs
Once you master this combo, locking down your .NET APIs becomes effortless.