How should I set net core api cors? - asp.net-web-api

I am coding an unofficial twitter api for myself. Then I send a get to this api using the console screen in my browser with the following method.
function httpGet(theUrl)
{
var xmlHttp = new XMLHttpRequest();
xmlHttp.open( "GET", theUrl, false ); // false for synchronous request
xmlHttp.send( null );
return xmlHttp.responseText;
}
such
httpGet(https://localhost:44311/WeatherForecast/alienationxs/true);
the problem is that when i do this via www.google.com it's ok and json data reaches me. but when I do it via twitter.com I get the following error.
via google.com
my cors settings on api
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddCors(options =>
options.AddDefaultPolicy(builder =>
builder.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin())); ;
services.AddMvc();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "twitterAPI", Version = "v1" });
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "twitterAPI v1"));
}
app.UseRouting();
app.UseCors(builder => builder
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
all i want is to reach my api via twitter.com just like google.com.

First, let's separate the flies from the cutlets.
Cross-Origin Resource Sharing (CORS) - is a separate security layer.
Content Security Policy (CSP) - is a separate security layer, it's appied before CORS. After passing through CSP yous can face with CORS if last one is breached.
As you can see from error message "... because it violates the following Content Security Policy directive ...", you faced with CSP violation therefore your CORS settings have no mean.
What's goin on.
You enter twitter.com web page and tries to execute connect request to localhost:44311 on behalf of twitter web page. But twitter's web page protected by CSP which forbid such requests:
Pay attention on 'connect-src' directive, which governs XMLHttpRequest().
The www.google.com web page does not have CSP, therefore you request on behalf of google does success.

The Twitter API does not support CORS.

Related

Problem in refreshing the login token from client to identity server

I have setup an Identity Server 4 on a .Net 6 web app. My web UI is another web app that is configured as the client of the Identity Sever. User is correctly refered to the login page when request accessing to a secured page/api and login is done OK. The solution also has other microservices that are also configured to use IS as oidc. The problem is after a while if I do not refresh the page, authentication fails when calling webapis. When I check the request, before the main call to the webapi controller, a request to the IS is made but is refused with CORS exception. I have configured the IS web app to accept CORS like this:
builder.Services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
});
and then:
app.UseCors("CorsPolicy");
What I am missing?
The mentioned settings did not solve the problem
The problem is might be caused from the expiration of the cookie or token (I'm not sure). But you should also add
builder.Services.AddSingleton<ICorsPolicyService>((container) => {
var logger = container.GetRequiredService<ILogger<DefaultCorsPolicyService>>();
return new DefaultCorsPolicyService(logger)
{
AllowedOrigins = { /*webUIOriginHere!NotUrl!*/ }
};
});
to the program.cs of Identity Server webapp and the problem should be solved.
Also adding AnyOrigin is dangerous. try doing something like this:
builder.Services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.WithOrigins( webUIOrigin )
.AllowAnyMethod()
.AllowAnyHeader());
});

angular2: asp.net core service throws 'No access control allow origin' when Windows Authentication is on

I have any angular2 app accessing asp.net core webapi service. It is working if webapi iis configuration is (Properties\launchSettings.json):
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:12246/",
"sslPort": 0
}
},
However, it throws the error once WindowsAuthentication is true and AnonymousAuthentication is false. The error:
XMLHttpRequest cannot load
http://localhost:12246/api//values/getSettings. No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:4200' is therefore not allowed
access. The response had HTTP status code 401.
Any idea please?
You are attempting to make a cross-origin request. This is permitted under the CORS specification, but requires configuration.
There are three steps to fixing this problem.
Configure both web servers to use Windows Authentication (and disable anonymous authentication). That is, both the server hosting your Angular 2 app and the server hosting your ASP.NET Core WebAPI app must be configured.
Enable CORS your ASP.NET Core WebAPI app:
in your Startup.cs file:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
if (env.IsDevelopment())
{
app.UseCors(builder =>
builder
.WithOrigins("http://localhost:4200") //<-- OP's origin
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials()
);
}
app.UseMvc();
}
Have Angular 2 send your credentials along with its CORS request:
import {Injectable} from '#angular/core'
import {Headers, Http, Response} from '#angular/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/toPromise';
#Injectable()
export class SomeAngularServiceThatCallsYourAPI{
constructor(private http: Http) { }
getApiData(): Promise<MyDataResult[]> {
var apiUrl = 'http://localhost:12246/api//values/getSettings';
return this.http.get(apiUrl,{
withCredentials: true
})
.toPromise()
.then(response => this.extractData(response) as MyDataResult[])
.catch(this.handleError);
}
}
For further details, see my blog post.

google ExchangeCodeForTokenAsync invalid_grant in webapi

i have implemented GoogleAuthorizationCodeFlow scenario from google api client dotnet and tutorial to get token from what my client sent to server as a code. but when i call flow.ExchangeCodeForTokenAsync , I get the following error :
{"Error:\"invalid_grant\", Description:\"\", Uri:\"\""}
I read google authorization invalid_grant and gusclass oauth 2 using google dotnet api client libraries but they didn't help me and. I think it must be very simple but I don't know why it doesn't work.
For client side , I have used Satellizer and this is my server Codes:
public bool PostExchangeAccessToken(GoogleClientAccessCode code)
{
string[] SCOPES = { "email" };
IAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets()
{
ClientSecret = "******",
ClientId = "********.apps.googleusercontent.com"
},
Scopes = SCOPES
});
try
{
TokenResponse token;
token = flow.ExchangeCodeForTokenAsync("*****#gmail.com", Newtonsoft.Json.JsonConvert.SerializeObject(code), "https://localhost:44301/",
CancellationToken.None).Result;
}
catch (Exception ex)
{
throw ex;
}
return true;
}
what is the problem?
On Github I found that I must use the Token from the client and use
GoogleAuthorizationCodeFlow.Initializer()
to create my UserCredential object.
You can check your google developer console settings.(Authorized redirect URIs)
Credentials => OAuth 2.0 client IDs => Your Application Settings => Authorized redirect URIs
You must add url. ("https://localhost:44301/")
My code :
flow.ExchangeCodeForTokenAsync("me", authCode, redirectUri, CancellationToken.None).Result;
Authorized redirect URIs
For use with requests from a web server. This is the path in your application that users are redirected to after they have authenticated with Google. The path will be appended with the authorization code for access. Must have a protocol. Cannot contain URL fragments or relative paths. Cannot be a public IP address.

WebAPI EnableCors with SupportsCredentials = true not working

I have an MVC site deployed to mysite.mydomain.co that authenticates against ADFS and creates an auth cookie:
public partial class Startup
{
public void ConfigureUserAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(WsFederationAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationType = WsFederationAuthenticationDefaults.AuthenticationType
});
app.UseWsFederationAuthentication(
new WsFederationAuthenticationOptions
{
MetadataAddress = ConfigurationManager.AppSettings["adsfs.MetadataAddress"],
Wtrealm = ConfigurationManager.AppSettings["adsfs.Wtrealm"]
});
}
}
There is also a WebAPI site deployed on "myapi.mydomain.com" with CORS enabled:
GlobalConfiguration.Configuration.EnableCors(new EnableCorsAttribute("https://mysite.mydomain.com", "*", "*") { SupportsCredentials = true });
The the user goes to mysite.mydomain.com. The MVC site authenticates against ADFS and I see the auth cookie being set with no problem.
My application is mostly an SPA, so from javascript there's a AJAX calls to myapi.mydomain.com using jQuery, setting the withCredentials option to true:
$.ajaxSetup({
xhrFields: { withCredentials: true }
});
That options is supposed to send security credentials (cookies) to the API. At runtime I don't see the cookies being set to the API and I get a 401 (unauthorized) error as expected.
If I run the same code on localhost (except for changing the origins to localhost of course) I see the cookie flowing to the API with no problem. My best guess is it works because it's the same subdomain (localhost) whereas on my servers is "mysite" vs "myapi".
Any ideas?

Call Play 2 REST API with AngularJS (CORS Problems)

I am developing an AngularJS application calling a REST API developed with Play Framework 2.2.0.
I have a problem related to Cross-domain ajax calls as the Angular application and the Play one will not be hosted on the same domain.
Here is the JS call in my Angular service :
$http
.post("http://localhost:9001/category/list", { langCode: 'fr-FR' })
.success(function(data, status, headers, config) {
callback(data.items);
})
.error(function(data, status, headers, config) {
console.log("Error Data : " + data);
console.log("Error Status : " + status);
});
Here is the route in my Play app :
POST /category/list controllers.catalog.ProductCategoryController.list()
If I don't send any data in the request, everything works fine
If I send data, I have Ajax errors concerning ACCESS_CONTROL_ALLOW_ORIGIN, ACCESS_CONTROL_ALLOW_HEADERS
The only workaround I have is the following :
Intercept all requests in Global class and add the headers
#Override
public Action onRequest(Request request, Method method) {
return new Action.Simple() {
#Override
public Promise<SimpleResult> call(Context ctx) throws Throwable {
Logger.debug("Intercepting request and applying CORS headers...");
ctx.response().setHeader(Controller.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
ctx.response().setHeader(Controller.ACCESS_CONTROL_ALLOW_HEADERS, "Content-Type");
return delegate.call(ctx);
}
};
}
Add another route with OPTIONS in routes
OPTIONS /category/list controllers.catalog.ProductCategoryController.list()
Is there a way of making the integration simpler than that ?
There's no CORS support out of the box in play; that's a situation I'd like to see changed, but for now you've identified a wart.
The good news is that you can manage a global workaround if you are OK having one CORS setting for all of your resources. It can be done in a couple of ways, one of which you identified. My inclination would be to go with a low level OPTIONS route.
Something like:
OPTIONS /*path controllers.Application.options()
From there, your handler definition can be something like:
Ok("").withHeaders(
"ACCESS_CONTROL_ALLOW_METHODS" -> "GET, POST, PUT, PATCH",
"ACCESS_CONTROL_ALLOW_HEADERS"->"Content-Type",
"ACCESS_CONTROL_ALLOW_ORIGIN" -> "*"
)
It's not super clean, but until Play adds something a bit more workable, I think it's your best option over making tons of OPTIONS routes (again, assuming you're comfortable with a global CORS setting)
You have to enable CORS support to your Play web server. The following url do have plenty of how-to for configurating server enabling the cross origin support:
http://enable-cors.org/server.html

Resources