ASP.NET Core Web API

JWT Authentication Setup

1 Views Updated 5/4/2026

Implementing Login & Generating Tokens

Now that the framework is configured to understand JWT tokens, we must build the mechanism that actually logs the user in and generates the token using System.IdentityModel.Tokens.Jwt.

1. The Login Controller

The login endpoint must be entirely exempt from Security (using the [AllowAnonymous] attribute), otherwise no one could ever log in!

[ApiController]
[Route("api/auth")]
public class AuthController : ControllerBase
{
    private readonly IConfiguration _config;
    private readonly IUserRepository _userRepo;

    public AuthController(IConfiguration config, IUserRepository userRepo)
    {
        _config = config;
        _userRepo = userRepo;
    }

    [HttpPost("login")]
    [AllowAnonymous]
    public async Task<IActionResult> Login([FromBody] LoginDto request)
    {
        // 1. Verify credentials against the DB (Using BCrypt/Identity Framework)
        var user = await _userRepo.FindByEmailAsync(request.Email);
        
        if (user == null || !BCrypt.Net.BCrypt.Verify(request.Password, user.PasswordHash))
        {
            return Unauthorized(new { error = "Invalid Credentials" });
        }

        // 2. Generate the Token String!
        var tokenString = GenerateJwtToken(user);
        
        return Ok(new { token = tokenString });
    }
}

2. Generating the Base64 Cryptographic JWT

Generating the token requires defining "Claims", setting an Expiration date, and digitally signing it using the Secret Key.

private string GenerateJwtToken(User user)
{
    // 1. Establish the Cryptographic Keys
    var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
    var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

    // 2. Define the Claims (The public data stored inside the token payload)
    var claims = new[]
    {
        // Standard JWT identifiers
        new Claim(JwtRegisteredClaimNames.Sub, user.Id.ToString()),
        new Claim(JwtRegisteredClaimNames.Email, user.Email),
        new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), 
        
        // Custom Role logic
        new Claim(ClaimTypes.Role, user.Role) // e.g. "Admin" or "User"
    };

    // 3. Assemble the Token 
    var token = new JwtSecurityToken(
        issuer: _config["Jwt:Issuer"],
        audience: _config["Jwt:Audience"],
        claims: claims,
        expires: DateTime.Now.AddMinutes(60), // Token dies strictly in 60 mins
        signingCredentials: credentials);

    // 4. Serialize to string format: "eyJhb..."
    return new JwtSecurityTokenHandler().WriteToken(token);
}

3. Consuming the Claims Securely in other Controllers

Once a user is successfully Authenticated and hits a protected endpoint like [Authorize], ASP.NET Core automatically unpacks their token and loads those claims into the User.Claims property inside the ControllerBase.

[ApiController]
[Route("api/profile")]
[Authorize] // Reject anyone without a valid token
public class ProfileController : ControllerBase
{
    [HttpGet("my-data")]
    public IActionResult GetMyProfile()
    {
        // Because the [Authorize] tag passed, we GUARANTEE the User object exists
        // Extract the Subject (User ID) from the claims we injected earlier!
        var userIdString = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        var userEmail = User.FindFirst(ClaimTypes.Email)?.Value;

        if (!int.TryParse(userIdString, out int userId)) return Unauthorized();

        var bankingData = _db.BankAccounts.Where(b => b.OwnerId == userId);
        return Ok(bankingData);
    }
}

4. Interview Mastery

Q: "Why is it absolutely critical that you rely on the `ClaimTypes.NameIdentifier` extracted from the Context, rather than allowing the frontend to pass the user ID in the URL (`/api/profile/my-data?userId=5`)?

Architect Answer: "That is the definition of an Insecure Direct Object Reference (IDOR) vulnerability. If your endpoint accepts the ID from the URL or JSON body, a malicious user can log in, get a valid token, but simply change the URL from `userId=5` to `userId=6`. The server sees they have a valid token, queries the DB for ID=6, and returns someone else's data. Because JWT Cryptography is impenetrable, the Claims inside the token are the ONLY source of truth. We extract the ID directly from the secure Token Claims on the server-side, guaranteeing that a user can only ever access data specifically tied to the ID they were authenticated against."

ASP.NET Core Web API
1. Fundamentals & HTTP
Introduction to ASP.NET Core Web API REST Principles and HTTP Methods Controllers & ControllerBase Routing (Attribute vs Conventional) Action Return Types (IActionResult)
2. Request Handling
Model Binding (FromQuery, FromBody, FromRoute) Dependency Injection (DI) Deep Dive App Settings & The Options Pattern
3. Data Access & Architecture
EF Core Setup in Web API DbContext & Migrations Repository & Unit of Work Pattern Asynchronous Programming (async/await)
4. Data Transfer & Validation
Data Transfer Objects (DTOs) & AutoMapper Model Validation (DataAnnotations) FluentValidation Integration
5. Advanced Concepts
Global Exception Handling Middleware Content Negotiation (JSON vs XML) Pagination & Filtering Advanced Searching & Sorting HATEOAS (Hypermedia) Implementation Output Caching & Response Caching
6. Security & Authorization
Cross-Origin Resource Sharing (CORS) JWT Authentication Setup Access Tokens & Refresh Tokens Workflow Role-Based & Policy-Based Authorization API Key Authentication Rate Limiting & Throttling
7. Documentation & Testing
Swagger & OpenAPI Configuration Customizing API Documentation Unit Testing Controllers (xUnit & Moq) Integration Testing (WebApplicationFactory)
8. Microservices & Deployment
Consuming External APIs (IHttpClientFactory) Health Checks & Diagnostics API Versioning Strategies Deploying APIs (Docker & Azure)