Apache Kafka Implementation in .NET – A Practical Microservices Guide

Modern enterprise applications demand high throughput, fault tolerance, and real-time data processing . Traditional synchronous APIs struggle at scale. This is where Apache Kafka becomes a backbone for event-driven microservices. In this article, we will explore Kafka implementation using .NET with real-world architecture, producer & consumer examples, and best practices. Why Kafka for Microservices? High-throughput event streaming Loose coupling between services Fault tolerance and durability Replayable event history Horizontal scalability Kafka Architecture – Core Concepts Kafka Components Producer – Publishes events Topic – Logical event stream Partition – Parallelism unit Broker – Kafka server Consumer Group – Scalable consumers Offset – Message position Kafka Microservices Architecture +-------------------+ | Web / API Layer | +-------------------+ | v +-------------------+ | Kafka Produce...

How I Built a JWT Refresh Token Middleware in ASP.NET Core

A few months ago, I was working on a project where our APIs were protected using JWT tokens. Everything was running smoothly… until we realized that our tokens were expiring too quickly. Users were getting logged out, and every client request needed re-authentication. That’s when I decided to implement a refresh token mechanism directly in middleware, and it turned out to be a neat solution.

Understanding the Problem

JWT tokens are stateless and self-contained, which is great for scalability, but the downside is that once they expire, the user needs a new token. Traditionally, developers handle this on the client side with a separate refresh endpoint. But I wanted a more transparent approach:

  • Whenever a valid token was nearing expiration, the server would automatically issue a new token.
  • This would reduce friction for the user.
  • The new token would be added to the response headers so clients could pick it up without changing the response payload.

Step 1: Creating Middleware to Intercept Responses

I started by creating a custom JWT middleware in ASP.NET Core. The middleware intercepts the HTTP response, reads the existing JWT token, and generates a new one if needed:


public class JwtTokenMiddleware
{
    private readonly RequestDelegate _next;
    private readonly AppSettings _appSettings;
    private readonly ILogger<JwtTokenMiddleware> _logger;

    public JwtTokenMiddleware(RequestDelegate next, IOptions<AppSettings> appSettings, ILogger<JwtTokenMiddleware> logger)
    {
        _next = next;
        _appSettings = appSettings.Value;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var originalBodyStream = context.Response.Body;

        try
        {
            using var responseBodyStream = new MemoryStream();
            context.Response.Body = responseBodyStream;

            await _next(context);

            await ProcessResponse(context, originalBodyStream);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error in JWT Token Middleware");
            context.Response.Body = originalBodyStream;
            throw;
        }
    }
}

Step 2: Checking and Generating a New Token

Inside the middleware, I created a method to generate a new JWT token when the old one is valid:


private string generateJwtToken(string tokenData, Claim claimData)
{
    ClaimsPrincipal principal;

    if (_appSettings.JwtAlgorithm == "RS256" && !string.IsNullOrEmpty(_appSettings.RsaPublicKey))
    {
        principal = JwtHelper.ValidateRs256Token(tokenData, _appSettings.RsaPublicKey, _appSettings.Issuer, _appSettings.Audience);
    }
    else
    {
        principal = JwtHelper.ValidateHmacToken(tokenData, _appSettings.Secret, _appSettings.Issuer, _appSettings.Audience);
    }

    if (principal?.Identity is ClaimsIdentity claimsIdentity)
    {
        if (claimData != null && !claimsIdentity.Claims.Any(x => x.Type == claimData.Type))
            claimsIdentity.AddClaim(claimData);

        var expires = DateTime.UtcNow.AddMinutes(_appSettings.Tokentimout.Value);

        if (_appSettings.JwtAlgorithm == "RS256" && !string.IsNullOrEmpty(_appSettings.RsaPrivateKey))
            return JwtHelper.GenerateRs256Token(claimsIdentity, _appSettings.Issuer, _appSettings.Audience, expires, _appSettings.RsaPrivateKey);
        else
            return JwtHelper.GenerateHmacToken(claimsIdentity, _appSettings.Issuer, _appSettings.Audience, expires, _appSettings.Secret);
    }

    return "";
}

Step 3: Appending the Token to the Response

Finally, I added the new token to the response headers so clients could pick it up automatically:


private async Task<string> AppendTokenToResponse(HttpContext context, string responseBody)
{
    var currentToken = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();

    if (!string.IsNullOrEmpty(currentToken))
    {
        var newToken = generateJwtToken(currentToken, null);

        context.Response.Headers.Add("X-New-Token", newToken);
    }

    return responseBody;
}

Step 4: Benefits I Observed

  • Users rarely experienced session expiration interruptions.
  • No extra refresh endpoints were needed in controllers.
  • The solution was stateless and scalable, perfect for microservices.
  • RS256 made it secure even if multiple services needed to validate tokens.

Key Takeaways

  • Middleware can automatically handle refresh tokens without changing API payloads.
  • Always validate tokens first before generating new ones.
  • Use RS256 for production APIs for strong security.
  • Adding tokens in response headers is safer than modifying JSON responses directly.

This experience taught me that sometimes the simplest solutions come from intercepting the response, not building new endpoints. By leveraging ASP.NET Core middleware, we were able to enhance security, improve user experience, and simplify token management.

Pro tip: If you’re using Angular, React, or mobile clients, make sure to capture the X-New-Token header in every response and update your client-side JWT automatically. That way, your users never get logged out unexpectedly.

Comments

Popular posts from this blog

How I Solved a Real Entity Framework Deadlock Caused by Different Table Order in High-Transaction Systems

Azure Service Bus vs Apache Kafka in .NET Microservices

A Practical Comparison Guide