My Cors Preflight Options Request Seems Slow - performance

I noticed that the "cors" check takes longer than I expected. This has happened at different speeds from localhost, qa and production.
I am using axios (^0.18.0) that is using mobx/MST/reactjs and asp.net core api 2.2
I can have preflight options that range from a 20 milliseconds to 10 seconds and it will randomly change.
For instance I have
https://localhost:44391/api/Countries
This is a get request and it can take 20 milliseconds 9 times in a row (me ctrl + F5) but on the 10th time it decides to take seconds (I don't get seconds really on localhost but sometimes a second).
So this test, the 204 (cors request) takes 215ms where the actual request that brings back the data takes half the time. This seems backwards.
This is my ajax request
const axiosInstance = axios.create({
baseURL: 'https://localhost:44391/api/Countries',
timeout: 120000,
headers: {
contentType: 'application/json',
}})
axiosInstance.get();
Here is my startup. I made cors all open and wanted to refine it after I get this issue solved.
public class Startup
{
public IHostingEnvironment HostingEnvironment { get; }
private readonly ILogger<Startup> logger;
public Startup(IConfiguration configuration, IHostingEnvironment env, ILogger<Startup> logger)
{
Configuration = configuration;
HostingEnvironment = env;
this.logger = logger;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseLazyLoadingProxies();
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
});
services.AddIdentity<Employee, IdentityRole>(opts =>
{
opts.Password.RequireDigit = false;
opts.Password.RequireLowercase = false;
opts.Password.RequireUppercase = false;
opts.Password.RequireNonAlphanumeric = false;
opts.Password.RequiredLength = 4;
opts.User.RequireUniqueEmail = true;
}).AddEntityFrameworkStores<ApplicationDbContext>().AddDefaultTokenProviders();
services.AddAuthentication(opts =>
{
opts.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
opts.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
opts.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(cfg =>
{
cfg.RequireHttpsMetadata = false;
cfg.SaveToken = true;
cfg.TokenValidationParameters = new TokenValidationParameters()
{
// standard configuration
ValidIssuer = Configuration["Auth:Jwt:Issuer"],
ValidAudience = Configuration["Auth:Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(Configuration["Auth:Jwt:Key"])),
ClockSkew = TimeSpan.Zero,
// security switches
RequireExpirationTime = true,
ValidateIssuer = true,
ValidateIssuerSigningKey = true,
ValidateAudience = true
};
});
services.AddAuthorization(options =>
{
options.AddPolicy("CanManageCompany", policyBuilder =>
{
policyBuilder.RequireRole(DbSeeder.CompanyAdminRole, DbSeeder.SlAdminRole);
});
options.AddPolicy("CanViewInventory", policyBuilder =>
{
policyBuilder.RequireRole(DbSeeder.CompanyAdminRole, DbSeeder.SlAdminRole, DbSeeder.GeneralUserRole);
});
options.AddPolicy("AdminArea", policyBuilder =>
{
policyBuilder.RequireRole(DbSeeder.SlAdminRole);
});
});
// do di injection about 30 of these here
services.AddTransient<IService, MyService>();
services.AddSingleton(HostingEnvironment);
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddTransient<IValidator<CompanyDto>, CompanyDtoValidator> ();
services.AddTransient<IValidator<BranchDto>, BranchDtoValidator>();
services.AddTransient<IValidator<RegistrationDto>, RegistrationDtoValidator>();
JsonConvert.DefaultSettings = () => {
return new JsonSerializerSettings()
{
NullValueHandling = NullValueHandling.Ignore,
MissingMemberHandling = MissingMemberHandling.Ignore,
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
};
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
//TODO: Change this.
app.UseCors(builder => builder
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseMvc();
}
}
I don't know if this is a valid test, but it does mimic what I am seeing in qa/production at random times.
I changed the axios request to ...
const axiosInstance = axios.create({
baseURL: 'https://localhost:44391/api/Countries/get',
timeout: 120000,
headers: {
contentType: 'application/json',
}})
axiosInstance.get();
basically I put /get, which causes a 404
Yet, when I refresh my page with the exact same scenario it is completed in milliseconds again (though still slower than the 404)
Edit
I made a hosted site nearly identical to my real site. The only difference is this one is only using http and not https.
http://52.183.76.195:82/
It is not as slow as my real site, but the preflight right now can take 40ms while the real request takes 50ms.
I am testing it in latest version of chrome and you will have to load up the network tab and load/click the button (no visual output is displayed).

I'm not sure if you are using a load balancer or some other proxy service, but one quick fix would be to move the CORS response headers to the load balancer level. This will minimize the overhead that your app may add at any given time. How to setup nginx as a cors proxy service can be found in How to enable CORS in Nginx proxy server?

Recently I tried to use Azure Front Door as a load balancer to solve this issue of CORS.
You can read about it here:
https://www.devcompost.com/post/using-azure-front-door-for-eliminating-preflight-calls-cors
Although I used Azure Front Door but you can use any load balancer which can repurposed as an ingress controller (like ng-inx) to solve the same problem.
The basic premise is that I am load balancing the UI hosted domain and the APIs under a same domain, thereby tricking the Browser into thinking it's a same origin call. Hence the OPTIONS request is not made anymore.

Related

SignOut does not redirect to site home page

I'm trying to setup an ASP.net Core 3 MVC app that uses OIDC to connect to my company's SSO portal (OpenAM).
I used Visual Studio 2019 project generator to create a basic app with no authentication and then I added the OIDC client capabilities following the steps at http://docs.identityserver.io/en/latest/quickstarts/2_interactive_aspnetcore.html#creating-an-mvc-client .
Logging in works great with minimal changes to the Startup class:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
// Setup Identity Server client
JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
options.Authority = "https://mycompany.com/ssoservice/oauth2";
options.RequireHttpsMetadata = false;
options.ClientId = "openIdClient";
options.ClientSecret = "secret";
options.ResponseType = "code";
options.ProtocolValidator.RequireNonce = false;
options.SaveTokens = true;
});
}
// 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();
IdentityModelEventSource.ShowPII = true;
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
// endpoints.MapDefaultControllerRoute();
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
I also set up a Logout controller action:
[Authorize]
public IActionResult Logout()
{
return SignOut("Cookies", "oidc");
}
The action actually works, i.e. when activated the cookie is deleted and the user is logged out from the SSO portal, but when the browser redirects to the /signout-callback-oidc endpoint it receives an HTTP 200 response without any content. I would have expected to have it automatically redirect to the site home page "/", which is the default value of the OpenIdConnectOptions.SignedOutRedirectUri property.
What am I missing?
Ok, after fiddling some more time, I found out this is the result of a missing draft implementation in the latest community OpenAM release (and also in the current paid ForgeRock AM, but they are working on it: https://bugster.forgerock.org/jira/browse/OPENAM-13831). Basically, the .net core handler for /signout-callback-oidc relies on having the state parameter available in order to redirect, like Ruard van Elburg mentioned in the comments:
https://github.com/aspnet/AspNetCore/blob/4fa5a228cfeb52926b30a2741b99112a64454b36/src/Security/Authentication/OpenIdConnect/src/OpenIdConnectHandler.cs#L312-L315
OpenAM does not send back the state parameter, as reported in my logs. Therefore, we need to perform the redirect ourselves - the most straightforward way seems to be using the OnSignedOutCallbackRedirect event:
Startup.cs
services.AddAuthentication(...)
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
...
options.Events.OnSignedOutCallbackRedirect += context =>
{
context.Response.Redirect(context.Options.SignedOutRedirectUri);
context.HandleResponse();
return Task.CompletedTask;
};
...
});
Thanks to all the users that replied to the discussion, your contributions allowed me to find the clues to the correct solution.
you return SignOut,
instead, SignOut user and return RedirectToAction("Home","Index")

Session cookie not being set on Edge (dot net core)

Session cookies are being set on Chrome, FireFox and even IE but not on Edge
The browser version is Microsoft Edge 42.17134.1.0
DotNet core version is 2.1
and the following information is used in my startup.cs file
public void ConfigureServices(IServiceCollection services) {
services.Configure < CookiePolicyOptions > (options => {
options.CheckConsentNeeded = context => false;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1).AddJsonOptions(options => {
options.SerializerSettings.ContractResolver = new Newtonsoft.Json.Serialization.DefaultContractResolver();
}).AddSessionStateTempDataProvider();
services.AddDistributedMemoryCache();
services.AddSession(o => {
o.IdleTimeout = TimeSpan.FromMinutes(80);
o.Cookie.HttpOnly = true;
o.Cookie.Name = "my-session-cookie";
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
if (env.IsDevelopment()) {
app.UseDeveloperExceptionPage();
} else {
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseSession();
app.UseSpaStaticFiles();
app.UseMvc(routes => {
routes.MapRoute(
name: "default",
template: "{controller}/{action=Index}/{id?}");
});
app.UseSpa(spa => {
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment()) {
spa.UseReactDevelopmentServer(npmScript: "start");
}
});
}
Here are some of the things I've tried out so far:
Adding the IsEssential condition to session options
Removing CookiePolicyOptions and UseCookiePolicy
Attempting to add an expiration date to the session cookie (didn't even start the solution)
Using fetch on Edge is causing the set-cookie header to not set a cookie on the browser
The solution was to add credentials: "same-origin" to the fetch options object
DOT NOT ADD IT TO THE HEADER
Quotes from HERE
By default, fetch won't send or receive any cookies
That means your have add the credentials object to it so it can set those cookies
Since Aug 25, 2017. The spec changed the default credentials policy to
same-origin.
I guess Edge have not implemented that default yet
Here's an example of a working fetch
fetch(link, {
body: JSON.stringify(myDataObject),
method: "POST",
credentials: "same-origin",
headers: {
"content-type": "application/json"
}
});
Open the Edge setting and click the "Advanced settings", under Cookies section, select "Under Cookies section" option, then re-test your application.
If still not working, try to reset your browser configuration to default and test your website again.

Asp.Net Core 2.1 WebApi returns 400 when sending request from Angular 6 WebApp to get access_token

I've got an application that is represented by asp.net core 2.1 web api on the server side and angular 6 on the client side. OpenIddict is used on the server side to support token authentication. The main problem is that when a request is sent from angular app to the server to generate or refresh access_token for a client, the server responds with the 400 (Bad Request), though when it is send from Postman everything works just fine. The Cors policy is added to allow corss-origin requests as client and server sides are placed on different ports so simple requests from angular to the server passes fine.
Here is the Startup class:
public class Startup
{
public Startup(IConfiguration configuration, IHostingEnvironment env)
{
Configuration = configuration;
hostingEnvironment = env;
}
public IConfiguration Configuration { get; }
private IHostingEnvironment hostingEnvironment { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContextPool<HospitalContext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
options.UseOpenIddict();
});
services.AddCors(options => options.AddPolicy("AllowLocalhost4200", builder =>
{
builder
.WithOrigins("http://localhost:4200")
.WithHeaders("Authorization", "Content-type")
.WithMethods("Get", "Post", "Put", "Delete");
}));
services.AddCustomIdentity();
services.AddCustomOpenIddict(hostingEnvironment);
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseCors("AllowLocalhost4200");
app.UseAuthentication();
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseMvc();
app.InitilizeDb();
}
}
and AddCustomOpenIddict method which is in ConfigureServices method if someone needs to see the configuration:
public static IServiceCollection AddCustomOpenIddict(this IServiceCollection services,
IHostingEnvironment env)
{
services.AddOpenIddict(options =>
{
options.AddEntityFrameworkCoreStores<HospitalContext>();
options.AddMvcBinders();
options.EnableTokenEndpoint("/connect/token");
options.EnableAuthorizationEndpoint("/connect/authorize");
options.AllowRefreshTokenFlow()
.AllowImplicitFlow();
options.SetAccessTokenLifetime(TimeSpan.FromMinutes(30));
options.SetIdentityTokenLifetime(TimeSpan.FromMinutes(30));
options.SetRefreshTokenLifetime(TimeSpan.FromMinutes(60));
if (env.IsDevelopment())
{
options.DisableHttpsRequirement();
}
options.AddEphemeralSigningKey();
});
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultForbidScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddOAuthValidation();
return services;
}
The Angular method that sends a request is:
public authorize(model: ILoginModel): Observable<Response> {
return this.http.post(`http://localhost:58300/connect/token`,
this.authService.authFormBody(model),
{headers: this.authService.authHeaders()});
}
with this.authService.authFormBody and this.authService.authHeaders:
authHeaders(): Headers {
const headers = new Headers(
{
'Content-Type': 'application/x-www-form-urlencoded'
});
return headers;
}
authFormBody(model: ILoginModel): string {
let body = '';
body += 'grant_type=password&';
body += 'username=' + model.email + '&';
body += 'password=' + model.password + '&';
body += 'scope=OpenId profile OfflineAccess Roles';
return body;
}
I'm actually new to token based authetication, so maybe there is a problem in configurations or something. Would appreciate any offers to solve a problem.
I found an error, it was just really that I removed AddPasswordFlow from my configs and left AllowRefreshTokenFlow() and AllowImplicitFlow() and was sending grant_type=password to the server that was not cofigured to accept such a grant, it was my mistake there. It is supposed to be:
services.AddOpenIddict(options =>
{
//some configs
options.AllowPasswordFlow()
.AllowRefreshTokenFlow()
.AllowImplicitFlow();
//some configs
});
For the begining, try allowing all headers:
https://learn.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-2.1#set-the-allowed-request-headers
Later, to be precise, look at all headers your angular app is sending and allow all of them in your cors policy. To start with, allow application-x-www-form-urlencoded
There seems to be a typo in your authFormBody method:
body += 'grant_type=password$';
This should be written as:
body += 'grant_type=password&';

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()
);
});

ASP.NET WEB API 2 OWIN Authentication unsuported grant_Type

Hi I am trying to set up OAuth bearrer token authentication in my ASP.NET Web API 2 project.
I have two project one will be the WEB API Project and the other a SPA project.
Here is what I have done so far:
I have created the OWIN Startup class:
[assembly: OwinStartup(typeof(CodeArt.WebApi.App_Start.Startup))]
namespace CodeArt.WebApi.App_Start
{
public class Startup
{
static Startup()
{
PublicClientId = "self";
UserManagerFactory = () => new UserManager<UserModel>(new UserStore<UserModel>());
OAuthOptions = new OAuthAuthorizationServerOptions {
TokenEndpointPath = new PathString("/Token"),
Provider = new OAuthAuthorizatonServer(PublicClientId, UserManagerFactory),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
AllowInsecureHttp = true
};
}
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
public static Func<UserManager<UserModel>> UserManagerFactory { get; set; }
public static string PublicClientId { get; private set; }
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalBearer);
app.UseOAuthBearerTokens(OAuthOptions);
}
}
I have configured Web API to use only bearer token authentication:
private static void ConfigureBearerTokenAuthentication(HttpConfiguration config)
{
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(Startup.OAuthOptions.AuthenticationType));
}
I have configured WEB API to support CORS:
private static void ConfigureCrossOriginResourseSharing(HttpConfiguration config)
{
var cors = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(cors);
}
I have created the OAuthAuthorizationServerProvider class.From this class I only managed to make my code call this method:
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
if(context.ClientId == null)
{
context.Validated();
}
return Task.FromResult<object>(null);
}
The if condition inside of it always gets executed.
On my spa project I have the following:
This is my viewModel:
var vm = {
grant_type: "password",
userName: ko.observable(),
password: ko.observable()
};
When the login button gets clicked I call this function:
var http = {
post:function(url, data) {
return $.ajax({
url: url,
data: data,
type: 'POST',
contentType: 'application/json',
dataType: 'jsonp'
});
}
}
function loginClick() {
var model = ko.mapping.toJS(vm.loginModel);
var rez = $.param(model);
http.post("http://localhost:3439/Token", rez)
.done(function (data) {
console.log(data);
})
.fail(function(eror, stuff, otherstuff) {
console.log(eror);
console.log(stuff);
console.log(otherstuff);
});
}
My first attempt I have set the post calls dataType to json and I got this errors:
OPTIONS ...:3439/Token 400 (Bad Request) jquery.js:7845
OPTIONS ...:3439/Token No 'Access-Control-Allow-Origin'
header is present on the requested resource. Origin
'...:3304' is therefore not allowed access.
jquery.js:7845
XMLHttpRequest cannot load ...3439/Token. No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin '...3304' is therefore not allowed
access.
The 3 dots represent http://localhost.
The second time arround I set it datatype to jsonp and I got back an error that stated unsupported "unsupported_grant_type".
Both calls make it to ValidateClientAuthentication that I mentioned above but they are both sent back as a failed request.
Now I am guessing that the problem is more related to how I am sending data instead of the grand_type because the SPA template in Visual Studion set's the grant type to grant_type: "password" like I did.
Also I have read that I have to serialize the data not send it in json in order for this to work here is the exact json serialized data that get's sent:
"grant_type=password&userName=aleczandru&password=happynewYear&moduleId=models%2FappPostModels%2FloginModel"
The model id property get's set to all my object in my SPA template by Durandal Framework.
Can anyone tell me what I am doing wrong I have been trying to figure this out for the last two days?
Add the following line of code to GrantResourceOwnerCredentials, which will add the header to the response.
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
for more information refer to:
web-api-2-0-cors-and-individual-account-identity
Like Robin Karlsson said, you should use:
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
in your Startup configuration.
And make sure it's the only cors statement (don't mix them) and the first statement in your Startup.

Resources