I created a DelegatingHandler to do some header manipulation for an HttpClient.  After deploying to production and getting more than one person to use the site, people were seeing information meant for someone else!  So I had to dig in and figure out what was happening.

string userId;

public OAuthMessageHandler(IHttpContextAccessor httpContextAccessor,
                           IOptions crmClientConfig,
                           IOptions azureAdConfig)
{
    var crmConfig = crmClientConfig.Value;
    var adConfig = azureAdConfig.Value;

    var claimsPrincipal = httpContextAccessor.HttpContext.User;
    var identity = (ClaimsIdentity)claimsPrincipal.Identity;
    userId = identity.Claims.FirstOrDefault(c => c.Type == "http://schemas.microsoft.com/identity/claims/objectidentifier")?.Value;

    authContext = new AuthenticationContext($"https://login.microsoftonline.com/{adConfig.TenantId}");
    credential = new ClientCredential(crmConfig.ClientId, crmConfig.ClientSecret);

    serviceUrl = crmConfig.Uri.ToString();
}

This is what I had originally.  I had the scope of the OAuthMessageHandler set to Transient in Startup.cs.  My problem was that I was using the userId in the SendAsync override of the handler.  And it wasn't the right userId!  Why not you ask.  You said you had the handler set as Transient.  You're right, I did, but there's a little detail that isn't very easy to find about DelegatingHandlers.  They are cached for a default of 2 minutes to improve performance.

So, in my naive implementation, I was using the first userId for 2 minutes after the DelegatingHandler was spun up.  To solve the problem, (which I should have done in the first place, instead of doing it in the constructor) was to move the retreival of the userId into the SendAsync override using the HttpContextAccessor that is injected into the class.

So now my constructor looks like this:

private readonly IHttpContextAccessor httpContextAccessor;

public OAuthMessageHandler(IHttpContextAccessor httpContextAccessor,
                           IOptions crmClientConfig,
                           IOptions azureAdConfig)
{
    var crmConfig = crmClientConfig.Value;
    var adConfig = azureAdConfig.Value;

    var this.httpContextAccessor = httpContextAccessor.HttpContext.User;

    authContext = new AuthenticationContext($"https://login.microsoftonline.com/{adConfig.TenantId}");
    credential = new ClientCredential(crmConfig.ClientId, crmConfig.ClientSecret);

    serviceUrl = crmConfig.Uri.ToString();
}

It's safe to use the HttpContextAccessor as that what it's designed to do, access the current request context.

Hope it helps!


Comment Section


Search