I have added my ASP.NET Core 6 MVC application to Azure "App registrations" and I'm able to login using the Microsoft WebUI and perform GET requests to obtain e.g. my views.
The application is hosted on IIS 8.5.
I've also defined a few App roles in my Azure App registration panel.
When I click on a button that sends a POST request though, a preflight request is sent and I see the following error:
Access to fetch at 'https://login.microsoftonline.com/[omitted]/oauth2/v2.0/authorize?client_id=[omitted]&redirect_uri=https%3A%2F%2F[omitted]%2Fsignin-oidc&response_type=id_token&scope=openid%20profile&response_mode=form_post&[omitted] from origin '[omitted]' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I have tried:
- Editing
web.configto exclude handlingOPTIONSrequests from the default ASP.NET Core handler and removing handlers likeExtensionlessUrlHandler-Integrated-4.0 - Adding
UseCorstoProgram.cs - Handling all
OPTIONSrequests inProgram.csso that they receive a 200 response - Checked the Web URIs I've added under Azure App Registrations (which should work, as I'm able to login...)
- Something else I don't recall (but from StackOverflow posts related to Cors, App registrations etc.)
But I've removed many of the above as they don't seem to work.
I don't understand why GET requests to my MVC controllers have no problem, while POST requests fail (because a preflight request is sent and hates me, while no preflight request is sent before a GET request).
Also, no SPA app is involved here, just plain MVC with views that reference ordinary vanilla js files (containing some fetch calls to POST).
Program.cs:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton... // adding my services like db repository etc.
JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
builder.Services
.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"));
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("Admin", policy => policy.RequireClaim("roles", "Admin"));
options.AddPolicy("Purchaser", policy => policy.RequireClaim("roles", "Purchaser", "Admin"));
options.FallbackPolicy = options.DefaultPolicy;
});
builder.Services.AddControllersWithViews(options =>
{
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
options.Filters.Add(new AuthorizeFilter(policy));
})
.AddMicrosoftIdentityUI();
var app = builder.Build();
//...
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
// app.MapAreaControllerRoute and stuff
app.Run();
web.config:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<location path="." inheritInChildApplications="false">
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="GET,POST,PUT,PATCH,DELETE" modules="AspNetCoreModuleV2" resourceType="Unspecified"/>
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit"/>
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit"/>
<remove name="ExtensionlessUrlHandler-Integrated-4.0"/>
<remove name="OPTIONSVerbHandler"/>
<remove name="WebDav"/>
<add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0"/>
<add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0"/>
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0"/>
</handlers>
<aspNetCore processPath="dotnet" arguments=".\TaskBridgePortal.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" hostingModel="inprocess"/>
</system.webServer>
</location>
<system.webServer>
<httpProtocol>
<customHeaders>
<!--add name="Access-Control-Allow-Methods" value="*" />
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Headers" value="*" /-->
</customHeaders>
</httpProtocol>
</system.webServer>
</configuration>
A sample controller:
[Authorize(Policy = "Orders")]
[Area/*..*/]
public class ReportController : Controller
{
public ReportController(/* ... */)
{
// ...
}
[HttpPost] // fails
public async Task<IActionResult> DownloadReportAsync([FromBody] DownloadReportRequest request)
{
// ...
}
public async Task<IActionResult> ReportAsync(bool returnView = true) // doesn't fail
{
//...
return View(/* ... */);
}
}
What am I missing?
You should register your CORS with the necessary URL ("*" for a lot of URLs)
Also, you can register CORS for static files
More information you can find below:
https://learn.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-6.0