CORS issue with an ASP.NET Core Web API using app.UseEndpoints - asp.net-web-api

I create a new project Microsoft Visual 2022 with the template ASP.NET Core 6.0 Web API with use of controllers.
The endpoint is https://localhost:7251/weatherforecast.
I set my program.cs:
string? origins = "_myAllowSpecificOrigins";
builder.Services.AddCors(options =>
{
options.AddPolicy(origins, builder => builder.WithOrigins("https://localhost:7041")
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
});
app.UseCors(origins);
Then I create a new project Microsoft Visual 2022 with the template Blazor Web Assembly App NET 6.0 without ASP.NET Core Hosted.
I write this basic call:
var client = new HttpClient();
var response = await client.GetFromJsonAsync<WeatherForecast[]>("https://localhost:7251/weatherforecast");
...and it works.
Now, for some project requirements, I have to replace in my API.program.cs
app.MapControllers();
with
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
Then I get an error
fetch is blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource
For info, if the web app is a Client/Server/Shared, there is no problem.
How can I set the communication web app not hosted/API using endpoints?

It turns out I just had to add in the Program.cs of the API
builder.Services.AddControllers();
Not using the default client/server template in my web app makes me skip some behaviors and requires this line.

Maybe it's could be related your response headers. Can you try add this;
app.Use(async (context, next) =>
{
context.Response.OnStarting(() =>
{
context.Response.Headers["Access-Control-Allow-Origin"] = "*";
context.Response.Headers["Access-Control-Allow-Credentials"] = "true";
return Task.CompletedTask;
});
await next();
});

Related

Blazor server hosted display alert if websockets not allowed

I would like to create a Blazor server hosted. Is there a way to display browser alert or redirect the user to another url, for example were Blazor Wasm is running, if SignalR cannot create a connection due to websockets not being allowed?
Can this be done using c# or Javascript?
You can do this with some Javascript implementing the following logic: Try to connect using Web Sockets, if fails, redirect.
Here is a my own working example in TypeScript:
public startConnection = () => {
this.hubConnection = new signalR.HubConnectionBuilder()
.configureLogging(signalR.LogLevel.Debug)
.withUrl('http://localhost:20000/yourHub', signalR.HttpTransportType.WebSockets)
.build();
this.hubConnection
.start()
.then(() => {
console.log('Connected!');
})
.catch(err => {
console.log('Error while starting connection: ' + err));
// do the redirect stuff here...
}
}
If you not familiar with Javascript you can start read the Microsoft Documentation for Javascript client.

When using an API route, return Http Response 401 instead of redirect to login page when not authorised

I'm building an ASP.NET Core 2.0 website using MVC and WebAPI to provide access to a series of microservices. Where a WebAPI controller requires a user to be authenticated and authorised (using the Authorize attribute), any unauthorised or not-logged in user gets the response back as the entire HTML for the MVC login page.
When unauthorised users access the API, I would like to return the HTTP status code 401 and its associated error message in the response, instead of an entire HTML page.
I've looked at a few existing questions and noticed that they either refer to ASP.NET MVC (such as SuppressDefaultHostAuthentication in WebApi.Owin also suppressing authentication outside webapi) which is no good for ASP.NET Core 2.0. Or they are using a hackaround for Core 1.x, which just doesn't seem right (ASP.Net core MVC6 Redirect to Login when not authorised).
Has a proper solution been implemented in Core 2.0 that anyone is aware of? If not, any ideas how it could be implemented properly?
For reference, there's part of a controller as an example:
[Authorize]
[ApiVersion("1.0")]
[Produces("application/json")]
[Route("api/V{ver:apiVersion}/Organisation")]
public class OrganisationController : Controller
{
...
[HttpGet]
public async Task<IEnumerable<string>> Get()
{
return await _organisationService.GetAllSubdomains();
}
...
}
And the configurations within Statup.cs:
public void ConfigureServices(IServiceCollection services)
{
...
// Add API version control
services.AddApiVersioning(options =>
{
options.ReportApiVersions = true;
options.AssumeDefaultVersionWhenUnspecified = true;
options.DefaultApiVersion = new ApiVersion(1, 0);
options.ErrorResponses = new DefaultErrorResponseProvider();
});
// Add and configure MVC services.
services.AddMvc()
.AddJsonOptions(setupAction =>
{
// Configure the contract resolver that is used when serializing .NET objects to JSON and vice versa.
setupAction.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
});
...
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
app.UseStatusCodePagesWithRedirects("/error/index?errorCode={0}");
app.UseStaticFiles();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
...
}
There is an easy way to suppress redirect to Login page for unathorized requests. Just add following call of ConfigureApplicationCookie extension method in your ConfigureServices:
services.ConfigureApplicationCookie(options =>
{
options.Events.OnRedirectToLogin = context =>
{
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
return Task.CompletedTask;
};
});
Or if you need custom error message in response body:
services.ConfigureApplicationCookie(options =>
{
options.Events.OnRedirectToLogin = async context =>
{
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
await context.Response.WriteAsync("Some custom error message if required");
};
});
As far as you're using redirects to custom error pages for error codes (UseStatusCodePagesWithRedirects() call in Configure method), you should add filter for 401 error. To achieve this, remove call to UseStatusCodePagesWithRedirects and use UseStatusCodePages extension method with skip of redirect for Unauthorized code:
//app.UseStatusCodePagesWithRedirects("/error/index?errorCode={0}");
app.UseStatusCodePages(context =>
{
if (context.HttpContext.Response.StatusCode != (int)HttpStatusCode.Unauthorized)
{
var location = string.Format(CultureInfo.InvariantCulture, "/error/index?errorCode={0}", context.HttpContext.Response.StatusCode);
context.HttpContext.Response.Redirect(location);
}
return Task.CompletedTask;
});
If you're using JWT for authentication with an ASP.NET Core 2 API; you can configure the unauthorized response when you're configuring the services for Authentication & JWT:
services.AddAuthentication( JwtBearerDefaults.AuthenticationScheme )
.AddJwtBearer(options => options.Events = new JwtBearerEvents()
{
OnAuthenticationFailed = c =>
{
c.NoResult();
c.Response.StatusCode = 401;
c.Response.ContentType = "text/plain";
return c.Response.WriteAsync("There was an issue authorizing you.");
}
});

How can I configure web api's http configuration in bit framework based apps?

I've configured web api using following code:
dependencyManager.RegisterWebApiMiddleware(webApiDependencyManager =>
{
webApiDependencyManager.RegisterWebApiMiddlewareUsingDefaultConfiguration();
});
How can I customize http configuration?
As you can see in https://docs.bit-framework.com/docs/bit-server-side/web-api.html#getting-started
You can write:
dependencyManager.RegisterWebApiMiddleware(webApiDependencyManager =>
{
webApiDependencyManager.RegisterWebApiMiddlewareUsingDefaultConfiguration();
webApiDependencyManager.RegisterGlobalWebApiCustomizerUsing(httpConfiguration =>
{
// You've access to web api's http configuration here
});
});

Google authentication with Angular2 and ASP.NET core Web API app

I'm creating an web application in Angular2 and i'd like to use Google for the user to login and use the application. Basically, once the user is logged in with Google, he will navigate in the site and make some AJAX call via Angular to the ASP.NET Core Web API.
In my mind, I though that all these calls should contain a JWT (issued by Google) and the ASP.NET Core Web Api would validate the token and then process the request if the token is valid. Simple enough... However, I'm struggling to find out how this could be achieved. All I found is posts talking about how to login via Google in order to use Google APIs. In my case, I don't need any Google API but the login one.
So far, I have this:
export class GoogleAuthenticationService {
private auth: any;
constructor(private userService: UserService) {
}
public bindLoginButton(element: any) {
this.ensureApiIsLoaded().then(() => {
this.auth.attachClickHandler(element, {}, (user) => {
let profile = user.getBasicProfile();
this.userService.set(profile.getName(), profile.getEmail(), user.getAuthResponse().id_token, AuthenticationType.Google);
});
});
}
// TODO: Better type definition of the promise
private ensureApiIsLoaded() {
return new Promise((resolve, reject) => {
gapi.load('auth2', () => {
this.auth = gapi.auth2.init({
client_id: "CLIENT_ID",
cookiepolicy: 'single_host_origin',
scope: 'profile email'
});
resolve();
});
});
}
}
Basically, I'm just initializing "gapi" with my client ID, then I define some properties in the "UserService". This works pretty well. Moreover, I'm using the value returned by user.getAuthResponse().id_token for every call to my web service (I did this following this article). The next step was to try and validate the token in C# (like the sample in Java) but I didn't find any valid resources talking about that and the class used in the Java code does not exists in C#...
Is it that complicated or am I just on the wrong path and this is totally not how the web application is supposed to work. Could someone give me some pointers about this scenario that is, according to me, quite common.

Cross origin SignalR connection stops after negotiate

I have an MVC 5 app serving up views, and a Web API 2 app as the service layer (.NET 4.5). The Web API app uses SignalR 2.1.2 to return progress as it's processing POSTs to the service API. The two are deployed to different domains, so I've set up cross origin support as per the asp.net tutorial article.
[assembly: OwinStartup(typeof (Startup))]
namespace MyApp.Service
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.Map("/signalr", map =>
{
//worry about locking it down to specific origin later
map.UseCors(CorsOptions.AllowAll);
map.RunSignalR(new HubConfiguration());
});
//now start the WebAPI app
GlobalConfiguration.Configure(WebApiConfig.Register);
}
}
}
WebApiConfig.cs also contains its own CORS declaration.
namespace MyApp.Service
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
//controller invocations will come from the MVC project which is deployed to a
//different domain, so must enable cross origin resource sharing
config.EnableCors();
// Web API routes
config.MapHttpAttributeRoutes();
//Snip other controller dependency initialisation
}
}
}
I've defined a simple hub class with no server-side API (it's only to allow the server to push to the clients, not for the clients to call into).
namespace MyApp.Service.Hubs
{
[HubName("testresult")]
public class TestResultHub : Hub
{
}
}
Since I'm going cross-domain AND the hub is not exposing any server side API, I'm not bothering to use a generated JS proxy.
The relevant bits of the JS that set up the signalr hub connection is: (remember this is being served up from the MVC app, which does not have any signalr support (except jquery-signalr-{version}.js of course))
function TestScenarioHandler(signalrHubUrl) {
var self = this;
//Snip irrelevant bits (mostly Knockout initialisation)
self.signalrConnectionId = ko.observable();
var hubConnection = $.hubConnection(signalrHubUrl, { useDefaultPath: false });
var hubProxy = hubConnection.createHubProxy("testresult");
hubProxy.on("progress", function(value) {
console.log("Hooray! Got a new value from the server: " + value);
});
hubConnection.start()
.done(function() {
self.signalrConnectionId(hubConnection.id);
console.log("Connected to signalr hub with connection id " + hubConnection.id);
})
.fail(function() {
console.log("Failed to connect to signalr hub at " + hubConnection.url);
});
}
Going cross-origin like this, Firefox network traffic shows (and I've confirmed Chrome shows the same thing) a GET to
http://****service.azurewebsites.net/signalr/negotiate?clientProtocol=1.5&connectionData=[{"name":"testresult"}]&_=1424419288550
Notice that the name matches the value of the HubName attribute on my hub class.
This GET returns HTTP 200, the response gives me a JSON payload containing a ConnectionId, ConnectionToken, and a bunch of other fields that suggests everything's ok. The HTTP response also has the Access-Control-Allow-Origin: header set to the domain that the GET originated from. All up it looks good, except that's where the traffic stops.
But the JS console prints "Failed to connect to signalr hub at http://****service.azurewebsites.net/signalr"
To verify I'm not doing anything too stupid, I've added signalr support and a basic hub to the MVC app (so no cross origin required), and changed the $.hubConnection() and hubConnection.createProxy() calls accordingly. When I do that, browser traffic shows the same /signalr/negotiate?... GET (obviously not cross origin any more), but then also GETs to /signalr/connect?... and /signalr/start?.... The JS console also prints a success message.
So in summary;
CORS is enabled on the service layer, and the signalr /negotiate GET returns 200, what appears to be a valid connection id, and the expected Access-Control-Allow-Origin: header. This suggests to me that the server-side CORS support is behaving itself correctly, but the signalr connection does not succeed.
When I reconfigure so the signalr connection is NOT cross origin, everything works as expected.
WTF am I missing or doing wrong?! Some conflict between HttpConfiguration.EnableCors() and IAppBuilder.UseCors(CorsOption) perhaps?
Solved it. I had changed the map.UseCors(CorsOptions.AllowAll) to pass in a CorsPolicy object instead, and set SupportsCredentials to false, having read elsewhere that Access-Control-Allow-Origin: * is incompatible with access-control-allow-credentials: true.
private static readonly Lazy<CorsOptions> SignalrCorsOptions = new Lazy<CorsOptions>(() =>
{
return new CorsOptions
{
PolicyProvider = new CorsPolicyProvider
{
PolicyResolver = context =>
{
var policy = new CorsPolicy();
policy.AllowAnyOrigin = true;
policy.AllowAnyMethod = true;
policy.AllowAnyHeader = true;
policy.SupportsCredentials = false;
return Task.FromResult(policy);
}
}
};
});
public void Configuration(IAppBuilder app)
{
app.Map("/signalr", map =>
{
map.UseCors(SignalrCorsOptions.Value);
map.RunSignalR(new HubConfiguration());
});
//now start the WebAPI app
GlobalConfiguration.Configure(WebApiConfig.Register);
}
Setting SupportCredentials to true results in the Access-Control-Allow-Origin header being rewritten with the actual origin (not *) and access-control-allow-credentials: true in the response.
And now it works.
For me following settings did good job
services.AddCors(c =>
{
c.AddPolicy("AllowCCORSOrigin", options => options
.WithOrigins("http://localhost:3000")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials()
);
});

Resources