tnk479
tnk479

Reputation: 794

How do I pass the windows authentication token on a web request to an api?

In ASP.NET Core 5, can I use Windows Authentication (which allows IIS to authenticate the user) and somehow grab that token and then use that to call a Web API application that also authenticates via Windows Authentication? In other words, we want the same security context in the API app as we have in the UI app. Is that even possible?

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(IISDefaults.AuthenticationScheme);
    var commonApiSettings = Configuration.GetSection("CommonApiSettings").Get<CommonApiSettings>(); 
    services.AddHttpClient("CommonApi",
                    client =>
                    {
                        client.BaseAddress = new Uri(commonApiSettings.BaseAddress);
                        client.DefaultRequestHeaders.Add("Accept", "application/json");
                        client.DefaultRequestHeaders.Add("User-Agent", "AspNetCore-Demo");
                    });
    services.AddControllersWithViews();
}

THen in my controller I want to call a web api. Every time I get 401 Unauthorized.

public class HomeController : Controller
{
    private readonly ILogger<HomeController> _logger;
    private readonly IHttpClientFactory _clientFactory;

    public HomeController(ILogger<HomeController> logger, IHttpClientFactory httpClient)
    {
        _logger = logger;
        _clientFactory = httpClient;
    }

    public async Task<IActionResult> Index()
    {

        IEnumerable<Confection> inventory;

        try
        {
            var user = (WindowsIdentity)User.Identity!;

            await WindowsIdentity.RunImpersonatedAsync(user.AccessToken, async () =>
            {

                var impersonatedUser = WindowsIdentity.GetCurrent();
                var message =
                    $"User: {impersonatedUser.Name}\t" +
                    $"State: {impersonatedUser.ImpersonationLevel}";

                var bytes = Encoding.UTF8.GetBytes(message);
          
                var httpClient = _clientFactory.CreateClient("CommonApi");
                httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", impersonatedUser.AccessToken.ToString());
                var request = new HttpRequestMessage(HttpMethod.Get, "");

                var httpResponseMessage = await httpClient.SendAsync(request);

                if (httpResponseMessage.IsSuccessStatusCode)
                {
                    using var contentStream = await httpResponseMessage.Content.ReadAsStreamAsync();
                    inventory = await JsonSerializer.DeserializeAsync<IEnumerable<Confection>>(contentStream);
                }
            });
        }
        catch (Exception e)
        {
            //await context.Response.WriteAsync(e.ToString());
        }

        return View();
    }

Upvotes: 2

Views: 8941

Answers (1)

Gabriel Luci
Gabriel Luci

Reputation: 40918

To send Windows credentials, you need to set the UseDefaultCredentials property of the HttpClientHandler used by HttpClient. There is an example of how to do this with IHttpClientFactory in the documentation.

In your case, it should look something like this:

services.AddHttpClient("CommonApi",
                client =>
                {
                    client.BaseAddress = new Uri(commonApiSettings.BaseAddress);
                    client.DefaultRequestHeaders.Add("Accept", "application/json");
                    client.DefaultRequestHeaders.Add("User-Agent", "AspNetCore-Demo");
                })
            .ConfigurePrimaryHttpMessageHandler(() =>
                new HttpClientHandler
                {
                    UseDefaultCredentials = true
                });

Upvotes: 4

Related Questions