Reputation: 144
I'm using ASP.NET Web API with OAuth authorization in my project.
I have tried to decouple every tier in the solution using best practices. I have a web project which contains AngularJS files and other resources which is uploaded on www.example.com and I have another project which is protected backend web api controllers and server side stuff which is uploaded on api.example.com
It all works fine in localhost. when I publish this to production server request for "/token" is successful but requesting any action in any controller in the back-end api returns this error: "Access to XMLHttpRequest at 'http://api.example.com/someRoute' from origin 'http://www.example.com' 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 searched almost any active link for similar error on the internet and no answers yet for me!
I paste some of my code here from the back-end API so you can understand my approach better.
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration httpConfig = new HttpConfiguration();
UnityConfig.Register(httpConfig);
ConfigureAuth(app);
WebApiConfig.Register(httpConfig);
app.UseWebApi(httpConfig);
#region AutoMapper Init
DtoMapping.Map();
#endregion
}
}
public void ConfigureAuth(IAppBuilder app)
{
// Configure the auth context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(AuthContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
//remove this line on production
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
Provider = new SimpleAuthorizationServerProvider()
};
// Token Generation
app.UseOAuthBearerTokens(OAuthServerOptions);
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
public class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
EnableCorsAttribute cors = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(cors);
config.MapHttpAttributeRoutes();
//...
}
}
[EnableCors("*", "*", "*")]
public class SimpleAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
return Task.FromResult<object>(null);
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
IList<string> roleNames;
using (var _repo = new IdentityRepository())
{
var user = await _repo.FindUserAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "username or password is invalid.");
context.Rejected();
return;
}
roleNames = await _repo.GetRolesForUserAsync(user.Id);
}
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
foreach (var roleName in roleNames)
{
identity.AddClaim(new Claim(ClaimTypes.Role, roleName));
}
var props = new AuthenticationProperties(new Dictionary<string, string>
{
{
"userName", context.UserName
}
});
var ticket = new AuthenticationTicket(identity, props);
context.Validated(ticket);
}
}
So, Can anyone please help me?
Thank you for your time.
Upvotes: 3
Views: 13520
Reputation: 179
Maybe a bit old but worth to mention that you need to add the cors headers in global.asax
protected void Application_BeginRequest(object sender, EventArgs e)
{
var context = System.Web.HttpContext.Current;
var origins = System.Configuration.ConfigurationManager.AppSettings["AllowedCorsDomain"]?.TrimEnd('/');
context.Response.AddHeader("Access-Control-Allow-Origin", origins);
if (context.Request.HttpMethod == "OPTIONS")
{
//These headers are handling the "pre-flight" OPTIONS call sent by the browser
context.Response.AddHeader("Access-Control-Allow-credentials", "true");
context.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, POST, DELETE, OPTIONS");
context.Response.AddHeader("Access-Control-Allow-Headers", "x-requested-with, Content-Type, origin, authorization, Accepts, accept, client-security-token, access-control-allow-headers");
context.Response.AddHeader("Access-Control-Max-Age", "86400");
context.Response.End();
}
}
Upvotes: 2
Reputation: 144
In case anyone wondered, this is how I solved this problem:
It was not about not setting the cors policy in asp.net web api (as I have mentioned that I did what Microsoft docs website was suggesting.)
The problem was that the IIS was not configured to handle OPTION verb methods! and because the pre-flight request was using OPTION method, It was always getting 404 not found (rejection from the web server), hence the error.
I should mention that to this day I still do not know why "/token" worked before the configuration of the web server and why controllers did not have the same reaction!
but anyhow, the problem just solved like that. Hope that helps!
Upvotes: 1