WebAPI OWIN SelfHosting CORS support - asp.net-web-api

I am using Microsoft.AspNet.WebApi V5.2.3, Microsoft.Owin.Host.HttpListener 3.0.1. Started with the following configuration:
public void Configuration(IAppBuilder appBuilder)
{
HttpConfiguration config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
appBuilder.UseWebApi(config);
}
And the API Controller class is declared using:
[EnableCors("*", "*", "*")]
public class CWebAPIController : ApiController
{
[Route("API/System/Login"), HttpGet]
public IHttpActionResult Login(string UserID, string Password)
{
.... bla bla bla .....
}
}
I am able to invoke the API above using HttpClent on the same computer, but if I trying to invoke the API from another computer (within the same subnet), I got 400 BadRequest response.
I believe this is CORS issue, but may I know how can I solve this. Searching in google yield many results and there are so many version / implementation of WebApi that I don't know which one is correct after trying one whole day.
Thanks in advance

EnableCors attribute should work on your API controller. Alternatively you can do one of the followings.
If you want to allow access from any computer then you can use
appBuilder.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
Else if you want to have control on which domain/computers to allow then use
var cors = new EnableCorsAttribute("www.example.com", "*", "*");
config.EnableCors(cors);
You'll need to install Microsoft.Owin.Cors package.

You need to bring in the Microsoft.Owin.Cors package, and maybe the Microsoft.AspNet.Cors package into your project and initialize CORS in your Startup class like this:
var corsPolicy = new CorsPolicy
{
AllowAnyHeader = true,
AllowAnyMethod = true,
AllowAnyOrigin = true,
SupportsCredentials = true
};
corsPolicy.ExposedHeaders.Add("x-markit-currentpage");
corsPolicy.ExposedHeaders.Add("x-markit-totalpages");
corsPolicy.ExposedHeaders.Add("x-markit-totalresults");
app.UseCors(new CorsOptions
{
PolicyProvider = new CorsPolicyProvider { PolicyResolver = context => Task.FromResult(corsPolicy) }
});

I have added cors like this:
public void Configuration(IAppBuilder app)
{
var config = ConfigureApi();
app.UseWebApi(config);
}
private HttpConfiguration ConfigureApi()
{
var config = new HttpConfiguration();
config.Routes.MapHttpRoute(
"DefaultApi",
"{controller}/{action}/{id}",
new { id = RouteParameter.Optional });
config.MessageHandlers.Add(new CorsMessageHandler ());
return config;
}
CorsMessageHandler is present here:
https://github.com/Microsoft/FetchClimate/blob/master/src/Service/Frontend/CORS/CorsMessageHandler.cs
Hope it will work.

For self hosting we need to run the command below in Admin mode.
netsh http add urlacl url=http://+:<port>/ user=<user id>

Related

CORS error on requesting OWIN token

I need to implement OWIN authorization from web api server. Below is my startup class.
[assembly: OwinStartup(typeof(SpaServerSide.MyStartUp))]
namespace SpaServerSide
{
public class MyStartUp
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
app.Map("/signalr", map =>
{
map.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
var hubConfig = new Microsoft.AspNet.SignalR.HubConfiguration { };
map.RunSignalR(hubConfig);
});
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/#")
});
OAuthAuthorizationServerOptions OAuthOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/Token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(5),
Provider = new SpaServerSide.Shared.OAuthTokenServer()
};
app.UseOAuthAuthorizationServer(OAuthOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
WebApiConfig.Register(config);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
}
}
}
Then, I implement the OAuthAuthorizationServerProvider as the following :
public class OAuthTokenServer : OAuthAuthorizationServerProvider
{
public ASPIdentityUserManager cusUserManager;
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Credentials", new[] { "true" });
var user = await cusUserManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "Username and password do not match.");
return;
}
var identity = await cusUserManager.CreateIdentityAsync(user, context.Options.AuthenticationType);
context.Validated(identity);
}
}
After that, I have hosted the web server on http://localhost:5587 and client web site on http://localhost. When I tried to request the token using Angular JS, the browser threw me an CORS error. The message is as follows :
Cross-Origin Request Blocked: The Same Origin Policy disallows reading
the remote resource at http://localhost:5587/Token. (Reason: CORS
header 'Access-Control-Allow-Origin' missing).
Please suggest me anything I would have missed.
Move the line:
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
at the beginning of your Configuration() method.
You have to configure CORS middleware before oauth middleware. And before signalr middleware if you need it.
Try this
Enable browser setting for allowing cross origin access
IE: http://www.webdavsystem.com/ajax/programming/cross_origin_requests
Firefox: How to enable CORS on Firefox?
Chrome: https://chrome.google.com/webstore/detail/allow-control-allow-origi/nlfbmbojpeacfghkpbjhddihlkkiljbi?hl=en
I think u need enable CORS in your server side. U can refer to this http://enable-cors.org/server.html . Click link based on your server.
Hope that help u. :)

Enable CORS for Web Api 2 and OWIN token authentication

I have an ASP.NET MVC 5 webproject (localhost:81) that calls functions from my WebApi 2 project (localhost:82) using Knockoutjs, to make the communication between the two projects I enable CORS. Everything works so far until I tried to implement OWIN token authentication to the WebApi.
To use the /token endpoint on the WebApi, I also need to enable CORS on the endpoint but after hours of trying and searching for solutions it is still now working and the api/token still results in:
XMLHttpRequest cannot load http://localhost:82/token. No 'Access-Control-Allow-Origin' header is present on the requested resource.
public void Configuration(IAppBuilder app)
{
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
TokenConfig.ConfigureOAuth(app);
...
}
TokenConfig
public static void ConfigureOAuth(IAppBuilder app)
{
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<AppUserManager>(AppUserManager.Create);
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider()
};
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
AuthorizationProvider
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
var appUserManager = context.OwinContext.GetUserManager<AppUserManager>();
IdentityUser user = await appUserManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
... claims
}
IdentityConfig
public static AppUserManager Create(IdentityFactoryOptions<AppUserManager> options, IOwinContext context)
{
// Tried to enable it again without success.
//context.Response.Headers.Add("Access-Control-Allow-Origin", new[] {"*"});
var manager = new AppUserManager(new UserStore<AppUser>(context.Get<ApplicationDbContect>()));
...
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider =
new DataProtectorTokenProvider<AppUser>(dataProtectionProvider.Create("ASP.NET Identity"));
}
return manager;
}
EDIT:
1. Important note is that opening the endpoint directly (localhost:82/token) works.
2. Calling the Api (localhost:82/api/..) from the webproject also works, so the CORS is enabled for WebApi.
I know your issue was solved inside comments, but I believe is important to understand what was causing it and how to resolve this entire class of problems.
Looking at your code I can see you are setting the Access-Control-Allow-Origin header more than once for the Token endpoint:
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
And inside GrantResourceOwnerCredentials method:
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
This, looking at the CORS specifications, is itself an issue because:
If the response includes zero or more than one Access-Control-Allow-Origin header values, return fail and terminate this algorithm.
In your scenario, the framework is setting this header two times, and understanding how CORS must be implemented, this will result in the header removed in certain circumstances (possibly client-related).
This is also confirmed by the following question answer: Duplicate Access-Control-Allow-Origin: * causing COR error?
For this reason moving the call to app.UseCors after the call to ConfigureOAuth allows your CORS header to be set only once (because the owin pipeline is interrupted at the OAuth middleware, and never reaches the Microsoft CORS middleware for the Token endpoint) and makes your Ajax call working.
For a better and global solution you may try to put again app.UseCors before the OAuth middleware call, and remove the second Access-Control-Allow-Origin insertion inside GrantResourceOwnerCredentials.
Follow below steps and you will have your API working:
Remove any code like config.EnableCors(), [EnableCors(header:"*"....)] from your API.
Go to startup.cs and add below line
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
before
ConfigureAuth(app);
Uou will also need to install Microsoft.owin.cors package to use this functionality
Solving the problem without using app.UseCors()
I had the same problem. I used a Vue.Js client with axois to access my REST-API with cross-corps. On my Owin-Api-Server I was not able to add Microsoft.Owin.Cors nuget due to version conflicts with other 3rd party components. So I couldn't use app.UseCors() method but I solved it by using the middleware pipeline.
private IDisposable _webServer = null;
public void Start(ClientCredentials credentials)
{
...
_webServer = WebApp.Start(BaseAddress, (x) => Configuration(x));
...
}
public void Configuration(IAppBuilder app)
{
...
// added middleware insted of app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.Use<MyOwinMiddleware>();
app.UseWebApi(config);
...
}
public class MyOwinMiddleware : OwinMiddleware
{
public MyOwinMiddleware(OwinMiddleware next) :
base(next)
{ }
public override async Task Invoke(IOwinContext context)
{
var request = context.Request;
var response = context.Response;
response.OnSendingHeaders(state =>
{
var resp = (IOwinResponse)state;
// without this headers -> client apps will be blocked to consume data from this api
if (!resp.Headers.ContainsKey("Access-Control-Allow-Origin"))
resp.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
if (!resp.Headers.ContainsKey("Access-Control-Allow-Headers"))
resp.Headers.Add("Access-Control-Allow-Headers", new[] { "*" });
if (!resp.Headers.ContainsKey("Access-Control-Allow-Methods"))
resp.Headers.Add("Access-Control-Allow-Methods", new[] { "*" });
// by default owin is blocking options not from same origin with MethodNotAllowed
if (resp.StatusCode == (int)HttpStatusCode.MethodNotAllowed &&
HttpMethod.Options == new HttpMethod(request.Method))
{
resp.StatusCode = (int)HttpStatusCode.OK;
resp.ReasonPhrase = HttpStatusCode.OK.ToString();
}
}, response);
await Next.Invoke(context);
}
}
So I created my own middleware and manipulated the response. GET calls only needed the Access-Control-Allow headers whereas for OPTIONS calls I also needed to manipulate the StatusCode because axois.post() is calling first with OPTIONS-method before sending the POST. If OPTIONS return StatusCode 405, the POST will never be sent.
This solved my problem. Maybe this can help somebody too.

IdentityServer3 and Web API in same process

I have Asp.net MVC web application and webapi in same project.I am using Resource owner Credential flow to have identity management.Is it possible to configure webapi, client and identity server in same startup.cs(startup.cs of webapplication).While trying to configure webapi and identity server in same startup.cs i ended up with the following error "An exception of type 'System.InvalidOperationException' occurred in IdentityServer3.AccessTokenValidation.dll but was not handled in user code
Additional information: IDX10803: Unable to create to obtain configuration from: 'https://localhost:44303/.well-known/openid-configuration'."
Here is the code of my startup.cs:
using IdentityServer3.AccessTokenValidation;
using IdentityServer3.Core.Configuration;
using IdentityServer3.Core.Services;
using IdentityServer3.Core.Services.Default;
using MarilynIdentityServer.IdentityServer;
using Microsoft.Owin;
using Owin;
using System;
//using System.IdentityModel.Claims;
using System.Security.Claims;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using System.Web.Http;
using System.Linq;
[assembly: OwinStartupAttribute(typeof(MarilynIdentityServer.Startup))]
namespace MarilynIdentityServer
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
var factory = new IdentityServerServiceFactory()
.UseInMemoryClients(Clients.Get())
.UseInMemoryScopes(Scopes.Get());
var userService = new UserLoginService();
factory.UserService = new Registration<IUserService>(resolver => userService);
factory.CorsPolicyService = new Registration<ICorsPolicyService>(new DefaultCorsPolicyService { AllowAll = true });
var option = new IdentityServerOptions
{
SiteName = "Embedded IdentityServer",
SigningCertificate = LoadCertificate(),
Factory = factory,
//AuthenticationOptions = new AuthenticationOptions
//{
// //EnableLocalLogin = false,
// IdentityProviders = ConfigureIdentityProviders
//},
};
app.UseIdentityServer(option);
app.Map("/api", idsrvApi =>
{
// token validation
idsrvApi.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
{
Authority = "https://localhost:44303/",
RequiredScopes = new[] { "sampleApi" }
});
// add app local claims per request
idsrvApi.UseClaimsTransformation(incoming =>
{
// either add claims to incoming, or create new principal
var appPrincipal = new ClaimsPrincipal(incoming);
incoming.Identities.First().AddClaim(new Claim("appSpecific", "some_value"));
return Task.FromResult(appPrincipal);
});
// web api configuration
var config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
idsrvApi.UseWebApi(config);
});
}
X509Certificate2 LoadCertificate()
{
return new X509Certificate2(
string.Format(#"{0}bin\identityServer\idsrv3test.pfx", AppDomain.CurrentDomain.BaseDirectory), "idsrv3test");
}
}
}
To conclude Is it possible to configure WebApi, Webapplication and Identity Server in same application?
Any help would be appreciated.
Regards
Amit
It definitely is possible, however I have experienced similar issues to yourself in not being able to find the local Identity Server.
What usually solves it is modifying your pipeline to have Identity Server in it's own app.Map block instead of the root. This seems to make it discoverable by the authentication middleware.
If you try this, don't forget to update the authority in you authentication middleware.
I achieved the goal stated by this question using a combination of Scott Brady's post and Andras Nemes's post, both excellent. Here's the Startup.cs that worked for me:
using IdentityServer3.Core.Configuration;
using Owin;
using System.Web.Http;
namespace idsvr3owin
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
// kick the Web API piece of this app
app.Map(
"/api",
apiApp => {
HttpConfiguration httpConfiguration = new HttpConfiguration();
// api.App_Start is a namespace in my app
api.App_Start.WebApiConfig.Register(httpConfiguration);
apiApp.UseWebApi(httpConfiguration);
}
);
// kick the IdentityServer3 piece of this app
app.Map(
"/identity",
identityApp =>
{
identityApp.UseIdentityServer(new IdentityServerOptions
{
SiteName = "Standalone Identity Server",
// idsvr3 is a namespace in my app
SigningCertificate = idsvr3.Cert.LoadCertificate(),
Factory = new IdentityServerServiceFactory()
.UseInMemoryClients(idsvr3.Clients.Get())
.UseInMemoryScopes(idsvr3.Scopes.Get())
.UseInMemoryUsers(idsvr3.Users.Get()),
RequireSsl = true
});
}
);
}
}
}

WebAPI : 403 Forbidden after publish website

Alright, I'm having a tough time locating the problem since it works locally but after doing a publish the results are simply:
Error Code: 403 Forbidden. The server denied the specified Uniform Resource Locator (URL). Contact the server administrator. (12202)
The code:
[RoutePrefix("api/v1/project")]
public class ProjectController : BaseApiController
{
[HttpGet]
public HttpResponseMessage GetProjects()
{
HttpResponseMessage resp = new HttpResponseMessage(HttpStatusCode.OK);
if(User.Identity.IsAuthenticated)
{
var model = new ModelFactory().CreateProjects();
resp = Request.CreateResponse(HttpStatusCode.OK, model);
}
return resp;
}
}
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// all actions under /project routes require authentication
config.Routes.MapHttpRoute(
name: "ProjectApi",
routeTemplate: "api/v1/{controller}/{action}/{apikey}",
defaults: new { apikey = RouteParameter.Optional },
constraints: new { controller = "project" },
handler: new BasicAuthHandler(config));
// all routes requires an api key
config.MessageHandlers.Add(new ApiKeyHandler());
config.MapHttpAttributeRoutes();
}
}
I've tried several "solutions" from the net yet none of them seems to fix this. I've added the:
// Stop IIS/Asp.Net breaking our routes
RouteTable.Routes.RouteExistingFiles = true;
from: http://www.grumpydev.com/2013/09/17/403-14-error-when-trying-to-access-a-webapi-route/
And also made sure that:
<modules runAllManagedModulesForAllRequests="true">
Having the code above, using the following link gives a successful connection where it checks (in the correct order) the APIkey (ApiKeyHandler), checks if the user needs to log in(BasicAuthHandler) and then goes to method in the controller ({controller}/{action}).
// THIS WORKS!
http://localhost:51077/api/v1/project/getprojects?apikey=123456
then we do a publish and tries the same thing
// This is haunted with number 403
http://website.com/api/v1/project/getprojects?apikey=123456
gives the Error Code: 403 Forbidden.
I am clueless. I've even tried changing the whole publish folder's security settings for "NETWORK SERVICE" to full access.. no change.
Let me know if you need any more intel.
Called the web server machine fellas and they had a firewall blocking incoming webapi calls with authenticating. It now works as it should :)

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