Blazor Server fails Authentication when consuming Same Origin Web API with Windows Authentication only - 401 Unauthorized

Viewed 782

I am calling a Web API from a Blazor Server application published on IIS as a IIS Application under the main site with Windows Authentication only on both. This means the the Same Origin rule is applied.

Although I'm sending a Network Credential with the request, Same Origin policy prevents the Credential from reaching the API and always get a

"Error: System.Net.Http.HttpRequestException: Response status code does not indicate success: 401 (Unauthorized)."

without any meaningful extra information.

The only workaround I found was to enable Anonymous Authentication on the main site which is Ok for the Staging environment but cannot be done on the Production environment.

On the API I need to know where the request is coming from and for that I need to know which user made the request.

How can I bypass the Same Origin rule and force the credential to go through to the API endpoint?

Here's what I've done:

On Startup.cs created an instance of HttpClient for Dependency Injection.

public void ConfigureServices(IServiceCollection services)
{
    [...]
    if (services.All(x => x.ServiceType != typeof(HttpClient)))
    {
        services.AddScoped(
            s =>
            {
                HttpClientHandler handler = new HttpClientHandler
                {
                    UseDefaultCredentials = false,
                    Credentials = Config.CredentialsCache,
                    PreAuthenticate = true,
                    AllowAutoRedirect = true,
                };
                HttpClient httpClient = new HttpClient(handler, false)
                {
                    BaseAddress = new Uri(Config.BaseUrl),
                };
                httpClient.DefaultRequestHeaders.Add("User-Agent", "MEC2SIGRE Dashboard");
                httpClient.DefaultRequestHeaders.Referrer = new Uri("http://mec2sigreqa.telecom.pt/Dashboard");
                httpClient.DefaultRequestHeaders.ConnectionClose = false;
                httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                return httpClient;
            });
    }

    services.AddScoped<MECDashBoardDataService>();
    [...]
}

(Based on Antti K. Koskela's Personal Professional Blog proposal.)

And the class:

public class MECDashBoardDataService
{
    private readonly HttpClient httpClient;

    public MECDashBoardDataService(HttpClient _httpClient)
    {
        httpClient = _httpClient;
    }

    public async Task<TaskersRow> MECTaskersDataAsync()
    {
        //******** The error is raised here **********//
        return await httpClient.GetFromJsonAsync<TaskersRow>(string.Concat(Config.BaseUrl, "/api/mec_api/gettaskersdata"));
    }
}

I guess it's a relative simple approach.

On the API:

[ApiController]
[Route("api/[controller]")]
public class MEC_APIController : ControllerBase
{
    [HttpGet]
    [ActionName("gettaskersdata")]
    [Route("gettaskersdata")]
    public async ValueTask<ActionResult<MECTaskersRow>> GetTaskersData()
    {
        await taskingScoped.InicializeTasking();
        MECTaskersRow MecTR = PushingDashboard.TaskersData;
        MecTR.Message = httpContextAccessor.HttpContext.User.Identity.Name; //Only for testing and prove that the credential doesn't reach this point
        return MecTR;
    }

    [HttpGet]
    public string Get()
    {
        return "Connecting OK.";
    }
}

On IIS: Image of IIS Manager

I've tried:

BrowserHttpMessageHandler.DefaultCredentials = FetchCredentialsOption.Include;

but couldn't find how to use it. I think it doesn't apply to Blazor Server.

Also tried:

System.Threading.ExecutionContext.SuppressFlow()

but it messes up Blazor!

Also tried with CORS but no luck here either.

Thanks in advance for any help I can get! :-)

0 Answers
Related