Can't enable CORS for web api - asp.net

I tried everything but can't enable CORS for my WebApi project. I guess I 'm missing something or not doing it right.
My StartUp.config is:
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
// Web API routes
//app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
config.MapHttpAttributeRoutes();
config.EnableCors(new System.Web.Http.Cors.EnableCorsAttribute("http://www.test.ca", "*", "GET,POST")); //enable only for this domain
ConfigureOAuth(app);
app.UseWebApi(config);
ConfigureAutofac(app, config);
}
My api controller:
[HttpPost]
[Authorize]
[Route("api/Accounts/GetTestTest")]
[System.Web.Http.Cors.EnableCors("http://www.test.ca", "*", "*")]
public HttpResponseMessage GetTestTest()
{
return this.Request.CreateResponse(System.Net.HttpStatusCode.OK);
}
Here I should be restricted because my request are made from MVC application which runs on localhost. Also I'm using tokens to authorize users.
Any ideas what I am missing or doing wrong?
EDIT Request is comming from MVC controller action like this:
static string CallApi(string url, string token, LogInRequest request)
{
System.Net.ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
using (var client = new HttpClient())
{
if (!string.IsNullOrWhiteSpace(token))
{
var t = Newtonsoft.Json.JsonConvert.DeserializeObject<CashManager.Models.Global.Token>(token);
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + t.access_token);
}
var response = client.PostAsJsonAsync<string>(url,string.Empty).Result;
return response.Content.ReadAsStringAsync().Result;
}
}

CORS does not apply to requests made from a back-end. It only applies to requests coming from browsers via AJAX.
You will need to do IP address-based filtering or something else to block requests from certain places. The authentication you have might be good enough though.

Related

SignalR gives 404 when using WebSockets or ServerSentEvents for some users but not for others

SignalR gives me 404 when trying to connect for some users. URLs are the same except for access_token.
It is stable reproducible per user (I mean that some users are stable OK, some users are stable 404).
access_token parsed jwt diff (left is OK user, right gets 404):
I did a trace level of logs and have next:
For the OK user:
For the user that gets 404:
Note: URLs under black squares are the same.
Front End is Angular 9 with package "#microsoft/signalr": "^3.1.8", and here's the code that builds the connection:
private buildHub(): HubConnection {
console.log(this.authService.accessToken);
let builder = new HubConnectionBuilder()
.withAutomaticReconnect()
.configureLogging(LogLevel.Information)
.withUrl('ws/notificationHub', {
accessTokenFactory: () => this.authService.accessToken
});
if (this.debugMode) {
builder = builder.configureLogging(LogLevel.Trace);
}
return builder.build();
}
Backend is using next code in Startup for configuring signalR hub:
In public void ConfigureServices(IServiceCollection services):
services.AddSignalR()
.AddJsonProtocol(options =>
{
options.PayloadSerializerSettings.ContractResolver = new DefaultContractResolver();
});
In public void Configure(IApplicationBuilder app, IHostingEnvironment env):
app.UseSignalR(route =>
{
route.MapHub<NotificationHub>("/ws/notificationHub");
});
Also we use custom authentication, so we have Authorize attribute for the Hub class:
[Authorize]
public class NotificationHub: Hub<INotificationHubClient>
and this code in public void ConfigureServices(IServiceCollection services):
services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = identityServerSettings.Url;
options.Audience = identityServerSettings.ApiScopeName;
options.RequireHttpsMetadata = identityServerSettings.RequireHttpsMetadata;
options.Events = new Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/ws"))
{
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
});
Unfortunately, I don't have the full access to the environment where it is reproducible, but I can request to see any settings or try to make some changes.
What else can I try to troubleshoot the issue?
UPDATE: negotiate is fine for both users.
I had this issue recently, after the size of my JWT increased. I found that in my case the 404 error was being thrown by IIS because the query string exceeded the limit of 2048. After increasing the query string max length, my issue was resolved.

Microsoft sign in works without redirect url

When I sign in with Microsoft OAuth in my Blazor app, authenticateResult.Succeeded is true, even if I don't specify a redirect URI. It's failing as intended for Google, if I don't add my URI to the OAuth client.
Imo it shouldn't work without that redirect URI, according to the OAuth2.0 spec:
The authorization server MUST require public clients and SHOULD
require confidential clients to register their redirection URIs.
I'm using Microsoft.AspNetCore.Authentication.MicrosoftAccount 3.0.3 with .NET Core 3.0
public class ExternalLoginModel : PageModel
{
public IActionResult OnGetAsync(string externalAuthType, string returnUrl)
{
var authenticationProperties = new AuthenticationProperties
{
RedirectUri = Url.Page("./externallogin",
pageHandler: "Callback",
values: new { returnUrl }),
};
return new ChallengeResult(externalAuthType, authenticationProperties);
}
public async Task<IActionResult> OnGetCallbackAsync(
string returnUrl = null, string remoteError = null)
{
var authenticateResult = await HttpContext.AuthenticateAsync("External");
if (!authenticateResult.Succeeded) // Should be false for Microsoft sign in
return BadRequest();
...
return LocalRedirect(returnUrl);
}
}
With the following added to my Startup:
services.AddAuthentication(o =>
{
o.DefaultSignInScheme = "External";
}).AddCookie("External");
services.AddAuthentication().AddGoogle(google =>
{
google.ClientId = Configuration["Authentication:Google:ClientId"];
google.ClientSecret = Configuration["Authentication:Google:ClientSecret"];
});
services.AddAuthentication().AddMicrosoftAccount(microsoftOptions =>
{
microsoftOptions.ClientId = Configuration["Authentication:Microsoft:ClientId"];
microsoftOptions.ClientSecret = Configuration["Authentication:Microsoft:ClientSecret"];
});
My App's Authentication settings look like this (I'm actually using localhost:12345 in the settings, but that's not what my app is running on..):
Ironically the last sentence might explain it, but I don't even know which flow the MicrosoftAccount library is using and I only get generic documentation when googling.
It fails as intended when using a completely different domain, not localhost with different ports. I guess that's good enough.
Additionally I unchecked "ID token" and "Treat application as a public client", therefore Authorization code flow should be used, to my understanding.

Blazor Request blocked by CORS policy

I am trying to send a request from a Blazor(client-side) client to a server and i keep getting this error:
Access to fetch at '[route]' (redirected from '[other route]') from
origin '[origin route]' has been blocked by CORS policy: No
'Access-Control-Allow-Origin' header is present on the requested
resource. If an opaque response serves your needs, set the request's
mode to 'no-cors' to fetch the resource with CORS disabled.
On the server i have already added the CORS extension in the pipeline to no avail:
Server Startup
public void ConfigureServices(IServiceCollection services) {
services.AddCors();
services.AddResponseCompression(options => {
options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[]
{
MediaTypeNames.Application.Octet,
WasmMediaTypeNames.Application.Wasm,
});
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
app.UseCors(x => x.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin().AllowCredentials());
app.UseResponseCompression();
app.UseMvc();
app.UseBlazor<Client.Startup>();
}
Blazor Client request
public async Task<Catalog> GetCatalogAsync() {
try {
HttpRequestMessage message = new HttpRequestMessage {
RequestUri = new Uri(BASE_PATH + Routes.GET_CATALOG), //BASE_PATH= 172.XX.XX.XX:8600
Method = HttpMethod.Get
};
var resp = await this.client.SendAsync(message); // client is HttpClient
var resultString = await resp.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<Catalog>(resultString);
return data;
} catch (Exception ex) {
throw;
}
}
Controller
[HttpGet]
[Route(Routes.GET_CATALOG)]
public async Task<Catalog> GetCatalogAsync() {
try {
var registry = await this.adminService.GetCatalogAsync();
return registry;
} catch (Exception ex) {
throw;
}
}
POCO
[Serializeable]
public struct Catalog{
}
What else can i do to be able to reach my server? Is it due to Blazor ?
As you can see i have already added the UseCors(...).
P.S
I have published my Blazor Server project together with the Client.They are in the same directory.This folder i placed it on a computer,and i am trying from my computer to open blazor : 172.168.18.22:8600/
Update
I have also tried adding headers to my HttpRequestMessage to no avail:
HttpRequestMessage message = new HttpRequestMessage {
RequestUri = new Uri(BASE_PATH + Routes.GET_CATALOG),
Method = HttpMethod.Get,
};
message.Headers.Add("Access-Control-Allow-Origin","*");
message.Headers.Add("Access-Control-Allow-Credentials", "true");
message.Headers.Add("Access-Control-Allow-Headers", "Access-Control-Allow-Origin,Content-Type");
#Bercovici Adrian, why do you add CORS support to your App ? Do you make cross origin requests ? If you don't, don't try to solve the issue by adding unnecessary configuration that may lead to more subtle bugs.
As usual, without seeing a repo of this app, can't help you any further.
Update:
What is this UseBlazor ?
You should upgrade your app to the latest version...
New Update:
Sorry, but I'm using the current preview version of Blazor
Startup class
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().AddNewtonsoftJson();
services.AddResponseCompression(opts =>
{
opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
new[] { "application/octet-stream" });
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseResponseCompression();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBlazorDebugging();
}
**// Instead of UseBlazor**
app.UseClientSideBlazorFiles<Client.Startup>();
app.UseStaticFiles();
app.UseRouting();
**// This configure your end points**
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
endpoints.MapFallbackToClientSideBlazor<Client.Startup>("index.html");
});
}
}
Note that I've removed the configuration of CORS as your client and server share the same domain. Please use the docs how to configure CORS appropriately.
Try this and see if it is working for you (I guess your issue is related to the configuration of the endpoints. Somehow, it seems to me that because you did not configure the endpoints, your request is redirected, and thus you get the message displayed by you above.)
Next to do is to check if your http request was appropriately cooked. But first checks the end points.
Somehow the problem was due to a very old client version that was cached on the browser.Never again will i forget to clear the browser cache after this problem.
Thank you all for your help and support !
Check that you do not send HTTP requests when running from HTTPS. For example if you send requests to http://172.168.18.22:8600 when your application was opened in https://172.168.18.22:8600 you may have an issue.
you need to specify your policy name in the middleware.
builder.Services.AddCors(policy =>{
policy.AddPolicy("Policy_Name", builder =>
builder.WithOrigins("https://*:5001/")
.SetIsOriginAllowedToAllowWildcardSubdomains()
.AllowAnyOrigin()
);});
// Configure the HTTP request pipeline.
app.UseCors("Policy_Name");

Access-Control-Allow-Origin error when using Owin

I'm have a aurelia client and a webserver. When i use localhost and i'm running on the same machine it works fine.
But when i want to access the server from another machine the page loads but the api calls give the following error:
No Access-Control-Allow-Origin header is present on the requested resource.
I'm using owin and to my undestanding i need to enable CORS for owin.
I did the follwing in my startup class:-
UPDATE
I have updated my class with input from Nenad but is still get the same error.
Below i have added the call from the client.
public void Configuration(IAppBuilder app)
{
this.container = new Container();
// Create the container as usual.
container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();
// Register your types, for instance using the scoped lifestyle:
container.Register<IWebDeps, WebDeps>(Lifestyle.Singleton);
// This is an extension method from the integration package.
container.RegisterWebApiControllers(GlobalConfiguration.Configuration, Assembly.GetExecutingAssembly());
container.Verify();
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
// Configure Web API for self-host.
var config = new HttpConfiguration()
{
DependencyResolver =
new SimpleInjectorWebApiDependencyResolver(container)
};
var cors = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(cors);
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
//// Custom Middleare
app.Use(typeof(CustomMiddleware));
app.UseWebApi(config);
//New code:
app.Run(context =>
{
context.Response.ContentType = "text/plain";
return context.Response.WriteAsync("Hello, world.");
});
}
My main program is calling the startUp class:-
using (Microsoft.Owin.Hosting.WebApp.Start<Startup>("http://localhost:8080"))
{
Console.WriteLine("Press [enter] to quit...");
Console.ReadLine();
}
Client code, 192.168.178.23 is the ip from the server.
let baseUrl2 = "http://192.168.178.23:8080/api/status/getStatus";
getStatus() {
return this.client.get(baseUrl2)
.then(response => {
return this.parseJSONToObject(response.content);
});
}
The error in Chrome:
XMLHttpRequest cannot load
http://192.168.178.23:8080/api/status/getStatus. No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:9000' is therefore not allowed
access. The response had HTTP status code 400.
Cors should be enabled now right? But i still get the error when doing a api call. Am i missing any steps? Our is this approah wrong?
Any suggestions are welcome!
You have to configure WebAPI to work with CORS.
Install Nuget package:
Install-Package Microsoft.AspNet.WebApi.Cors
Enable CORS on HttpConfiguration object:
config.EnableCors();
Add [EnableCors] attribute on your controller:
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Cors;
namespace WebService.Controllers
{
[EnableCors(origins: "www.example.com", headers: "*", methods: "*")]
public class TestController : ApiController
{
// Controller methods not shown...
}
}
or register it globally via HttpConfig:
var cors = new EnableCorsAttribute("www.example.com", "*", "*");
config.EnableCors(cors);
More details at: Enabling Cross-Origin Requests in ASP.NET Web API 2
Add this line and check,
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
and remove,
var cors = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(cors);
this worked for me.
Turns out that when starting OWIN the adres should be http://*:8080. Instead of local host.

OWIN token authentication 400 Bad Request on OPTIONS from browser

I am using token authentication for small project based on this article: http://bitoftech.net/2014/06/09/angularjs-token-authentication-using-asp-net-web-api-2-owin-asp-net-identity/
Everything seems to work fine except one thing: OWIN based token authentication doesn't allow OPTIONS request on /token endpoint. Web API returns 400 Bad Request and whole browser app stops sending POST request to obtain token.
I have all CORS enabled in application as in sample project. Below some code that might be relevant:
public class Startup
{
public static OAuthBearerAuthenticationOptions OAuthBearerOptions { get; private set; }
public void Configuration(IAppBuilder app)
{
AreaRegistration.RegisterAllAreas();
UnityConfig.RegisterComponents();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
HttpConfiguration config = new HttpConfiguration();
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
ConfigureOAuth(app);
WebApiConfig.Register(config);
app.UseWebApi(config);
Database.SetInitializer(new ApplicationContext.Initializer());
}
public void ConfigureOAuth(IAppBuilder app)
{
//use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseExternalSignInCookie(Microsoft.AspNet.Identity.DefaultAuthenticationTypes.ExternalCookie);
OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(60),
Provider = new SimpleAuthorizationServerProvider(),
RefreshTokenProvider = new SimpleRefreshTokenProvider()
};
// Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
}
}
Below is my login function from javascript (I am using angularjs for that purpose)
var _login = function (loginData) {
var data = "grant_type=password&username=" + loginData.userName + "&password=" + loginData.password;
data = data + "&client_id=" + ngAuthSettings.clientId;
var deferred = $q.defer();
$http.post(serviceBase + 'token', data, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }).success(function (response) {
localStorageService.set('authorizationData', { token: response.access_token, userName: loginData.userName, refreshToken: response.refresh_token, useRefreshTokens: true });
_authentication.isAuth = true;
_authentication.userName = loginData.userName;
_authentication.useRefreshTokens = loginData.useRefreshTokens;
deferred.resolve(response);
}).error(function (err, status) {
_logOut();
deferred.reject(err);
});
return deferred.promise;
};
var _logOut = function () {
localStorageService.remove('authorizationData');
_authentication.isAuth = false;
_authentication.userName = "";
_authentication.useRefreshTokens = false;
};
I've lost some time on this problem today. Finally i think i've found a solution.
Override method inside your OAuthAuthorizationServerProvider:
public override Task MatchEndpoint(OAuthMatchEndpointContext context)
{
if (context.IsTokenEndpoint && context.Request.Method == "OPTIONS")
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Headers", new[] { "authorization" });
context.RequestCompleted();
return Task.FromResult(0);
}
return base.MatchEndpoint(context);
}
This appears to do three necessary things:
Force auth server to respond to OPTIONS request with 200 (OK) HTTP status,
Allow request to come from anywhere by setting Access-Control-Allow-Origin
Allows Authorization header to be set on subsequent requests by setting Access-Control-Allow-Headers
After those steps angular finally behaves correctly when requesting token endpoint with OPTIONS method. OK status is returned and it repeats request with POST method to get full token data.
Override this method inside your OAuthAuthorizationServerProvider:
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
Are you running it locally or are you publishing it to Azure like in the blog article's sample code?
If you're running it on Azure, you can easily fix CORS problems by enabling CORS in the Azure portal:
Click on your App Service in the Azure Portal to enter the management screen.
In the list of management options, scroll down to the 'API' section, where you will find the 'CORS' option. (Alternatively type 'CORS' in the search box).
Enter the allowed origin, or enter '*' to enable all, and click save.
This fixed the OPTIONS preflight check problem for me, which a few other people seem to have had from the code in that particular blog article.
Solved it. The problem was not sending with OPTIONS request header Access-Control-Request-Method
This should do the trick:
app.UseCors(CorsOptions.AllowAll);

Resources