Having an issue with an AspNet.Core app that is using Windows Authentication and CORS. Windows auth works fine for direct API calls. But client fails to initiate negotiate after an OPTIONS request (the API returns 401 with www-authenticate Negotiate, and the client fails).
I’ve simplified the app down to a small sample.
Application Setup:
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme).AddNegotiate();
builder.Services.AddAuthorization();
builder.Services.AddCors(options =>
{
options.AddPolicy("CorsPolicy", builder => builder
.AllowAnyMethod()
.AllowAnyHeader()
.WithOrigins("https://<my origin>")
.WithExposedHeaders("WWW-Authenticate")
.AllowCredentials()
);
});
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.WebHost.ConfigureKestrel(options =>
{
options.ListenAnyIP(5001, listenOptions =>
{
listenOptions.UseHttps("<my cert>", "<my secret>");
});
});
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseMiddleware<RequestLoggingMiddleware>();
app.UseMiddleware<ResponseLoggingMiddleware>();
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseCors("CorsPolicy");
app.MapControllers();
app.Run();
}
Controller:
[ApiController]
[Route("[controller]")]
public class IwaController : ControllerBase
{
private readonly ILogger<IwaController> _logger;
public IwaController(ILogger<IwaController> logger)
{
_logger = logger;
}
[HttpGet]
[Authorize]
[Route("[action]")]
public IActionResult SiteCheck()
{
var identity = User?.Identity;
if (identity == null || !identity.IsAuthenticated)
{
_logger.LogWarning("SiteCheck: Unauthorized request: " + identity?.IsAuthenticated.ToString() ?? "Null");
return Unauthorized();
}
_logger.LogInformation("SiteCheck: Authorized request: {name} - {type}", identity?.Name, identity?.AuthenticationType);
return new JsonResult(new { identity?.Name, identity?.AuthenticationType });
}
}
Invoking ‘sitecheck’ directly yields an expected windows auth via negotiate. Doing a redirect from ” has a success OPTIONS request (204), with apparent appropriate headers:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET
Access-Control-Allow-Origin: https://<my origin>
Date: Thu, 27 Jun 2024 00:10:42 GMT
Server: Kestrel
But the subsequent GET, doesn’t do the negotiate, it just fails with 401, even though the negotiate header is present in the response:
Content-Length: 0
Date: Thu, 27 Jun 2024 00:10:42 GMT
Server: Kestrel
Www-Authenticate: Negotiate
I’ve tried a number of different approaches with middleware ordering, custom Authorization handlers, and CORS policy, as well as a bunch time googling, in Chat GPT, and searching stackoverflow to no avail.
Would appreciate any suggestions or ideas anyone could provide.
Mike Nicolino is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.