0

I have multi-tenant Web API application built using ASP.NET Core 6. We are using HangFire for Report Scheduling. It uses Onion based Architecture where Interface lies in the application and implementation outside the layer

Project has application Layer and Integration Layer , so IDbContext will be present in app layer and implementation class DbContext lies in Integration Layer, so I can not create dbContext instance directly from app layer as it is not accessible.

As it is web API , we use jwt token and this token is passed as part of "Http Request Header" for every other request for user to authorize.

As background jobs run , this HttpContext will be null which ends up giving error for all services as most of the DI services use this to get the values like connection String for DbContext, logged in user, URL to third party services as all are stored as part of that HttpHeader.

 public jmasdbContext(DbContextOptions<jmasdbContext> options, ITenantProviderService tnt, IJwtAuthenticationManager jwt, IWebHostEnvironment env)
        : base(options)
    {
        //Check if is developement is added for migration purpose.- THIS IF CONDITION CAN BE REMOVED WHILE DEPLOYING TO PRODUCTION
        if (!env.EnvironmentName.Equals("Development"))
        {
            //This if part will be executed only during Login creation as Claims will not be set at this time, 
            //we need to fetch ConnectionString from Tenant...
            if (tnt != null && tnt.HasTenantSet())
            {
                ConnectionString = tnt.Tenant.ConnectionString;
            }
            else if (jwt != null && !string.IsNullOrEmpty(jwt.GetAccessToken()))
            {
                ConnectionString = jwt.GetTenantConnectionString();
            }
        }
    }

Here is how the connectionString is Set. First part gets executed during setting the tenant (login) and else part for every subsequent request

The code jwt.GetAccessToken() is dependent on httpContext and jwt.GetTenantConnectionString() is dependent on the jwt.GetAccessToken as it is stored as part of token claims.

/// <summary>
    /// Gets the token passed in Header
    /// </summary>
    /// <returns>Access Token</returns>
    public string GetAccessToken()
    {
        
        if (_httpContextAccessor != null && _httpContextAccessor.HttpContext != null)
        {
            return _httpContextAccessor.HttpContext.Request.Headers["Authorization"];
        }

        if (!string.IsNullOrEmpty(AccessToken))
        {
            return AccessToken;
        }

        return string.Empty;
    }

To get the connectionString for the Tenant

/// <summary>
    /// Get Tenant's connection string
    /// </summary>
    /// <returns></returns>
    public string GetTenantConnectionString()
    {
        var customerDomain_Name = GetCustomerDomain();
        var keyName = customerDomain_Name + ":DataContext";
        var connectionString = _configuration.GetValue<string>(keyName);
        return connectionString;
    }

I tried faking using "https://stackoverflow.com/questions/47404479/add-httpcontext-into-hangfire" and other Unit test ways but nothing seem to work and defining HttpContext in .NET Core has been changed and I can not create HttpContext manually and need to be injected via IHttpContextAccessor.

Do you see any ways this can be achieved?

RashmiMs
  • 129
  • 14
  • Remove the dependency on HttpContext from any code that you intend to execute from a background service. That's the proper way to handle this, rather than trying to fake the HttpContext itself. What's preventing you from doing that? – mason Dec 27 '22 at 18:24
  • @mason - That would be like removing most of the main logic which might affect the other regular APIs. JWT token being passed as part of httpContext request header is like backbone for every subsequent request that happens, so we can not remove those parts of the code. – RashmiMs Dec 27 '22 at 19:41
  • Why not? You have code that is intended to be run as part of a background process where no HTTP request is present. Therefore there will be no HttpContext. So, the proper solution is to write that code to be flexible in where it gets its dependencies from so that the JWT doesn't have to be passed via HttpContext, but can instead be retrieved in another manner. If that code is going to be invoked from your background service and ALSO from a context that has an HttpContext, then make the logic for grabbing the JWT flexible by abstracting it: perhaps creating an interface IJwtProvider. – mason Dec 27 '22 at 19:45
  • Then you'd create a concrete implementation of IJwtProvider for running when there's an HttpContext, and a separate implementation that runs when there's no HttpContext (such as in a Hangfire background job). This will be far simpler than trying to fake an HttpContext. – mason Dec 27 '22 at 19:46
  • @mason, what I meant is I can not remove the code but can make those dependency method flexible to work for background service. This is what I have done, passed jwt token to the background service method, when method gets executed during background process, I set this token to the jwt service , this jwt service has methods to get the connection string for context using claims, used that method and set the connection string for _context. – RashmiMs Dec 29 '22 at 06:13

0 Answers0