Publish web site with CORS enabled - asp.net

I'm diving into the deployment of websites for the first time. I'm making Cross Origin Request (CORS) in the web api controller from an Angular controller on the client. For development, I've set the EnableCors attribute on the Web Api controller, but obviously, that's pointing to a site on my local machine. I'm trying to figure out how to easily transform that setting as I move it to a hosted production site.

Enable CORS For All Domains
You first option is to enable CORS for all domains. This might not be the most secure option if, for example, you know that your API will be accessed only from a pre-defined set of web sites (e.g. your Angular app). But it some cases it is OK to enable CORS globally.
You can do it from WebApiConfig:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Enable CORS globally for all routes
var enableCorsAttribute = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(enableCorsAttribute);
// Other configurations
}
}
Or enable CORS support in config and then use EnableCors attribute on specific controllers/actions:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.EnableCors();
// Other configurations
}
}
public class ValuesController : ApiController
{
[HttpGet]
[Route("api/values")]
[EnableCors(origins: "http://www.example.com", headers: "*", methods: "*")]
public string[] GetValue()
{
}
}
Enable CORS From Azure Portal
If host in Azure, I think Web Apps now allow you to enable CORS support and to specify allowed domains right from the Azure Portal:
Enable CORS Based on App Settings
Another option is to enable CORS for domains that can be configured from App Settings. This way you can change allowed domains for different API instances using web.config transforms, deployment token injection, or just Azure App Settings. This can be easily achieved by creating your own attribute that implements ICorsPolicyProvider interface:
// The implementation below supports only a single origin and
// doesn't allow you to specify allowed headers or request types.
// But it can be easily extended to support these scenarios as well.
public class EnableCorsWithConfigAttribute : Attribute, ICorsPolicyProvider
{
private readonly string configKey;
public EnableCorsWithConfigAttribute(string configKey)
{
this.configKey = configKey;
}
public Task<CorsPolicy> GetCorsPolicyAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
var policy = new CorsPolicy
{
AllowAnyOrigin = false,
AllowAnyHeader = true,
AllowAnyMethod = true,
};
if (ConfigurationManager.AppSettings
.AllKeys
.Contains(configKey))
{
var origin = ConfigurationManager.AppSettings[configKey];
if (!origins.IsNullOrWhitespace())
{
policy.AllowAnyOrigin = origins.Equals("*");
if (!policy.AllowAnyOrigin) policy.Origins.Add(origin);
}
}
return Task.FromResult(policy);
}
}
Then you can use it as follows:
public class ValuesController : ApiController
{
[HttpGet]
[Route("api/values")]
[EnableCorsWithConfig("Application:Cors:AllowedOrigin")]
public string[] GetValue()
{
}
}

Related

API, in same project (Blazor server app) with Windows Authentication, not authorizing roles

Blazor server app (.net 6.0) which uses Windows Authentication and works properly with Authorizing Roles is not recognizing Authorize Roles in an API I have in the same project. If I have allow anonymous in iis set and use [AllowAnonymous] in API it works fine. (Making an api call from code in same project). If iis does not allow anonymous then I try to use [Authorize(Roles.... but I always get 401 Unauthorized.
I do pass in headers username and password that do a validation that works fine if api set for allowanonymous but if not set then the api Get method never even gets triggered and returns 401.
Shouldn't the API recognize the Authorization? Do I need to configure something different in the project?
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class SampleController : ControllerBase
{
private readonly IConfiguration _config;
private readonly IDataServices _iService;
public SampleController(IDataServices dataAccess, IConfiguration config)
{
this._iService = dataAccess;
_config = config;
}
// GET: api/<SampleController>
[HttpGet]
[Authorize(Roles = "Administrators")]
public async Task<List<SampleModel>> GetAllItems([FromHeader] string username, [FromHeader] string password)
{
List<SampleModel> list = new List<SampleModel>();
if (username == _config.GetValue<string>("SampleSettings:username") && password == _config.GetValue<string>("SampleSettings:password"))
{
list = await _iService.GetList();
}
return list;
}
}
thanks,

ASP.NET Cors > allow only one origin

We have been looking a lot on StackOverflow for this but we cannot seem to have it working.
Our scenario is as following. We have server A and server B.
Server A is a web API that communicates with Server B which is also a web API.
Server B should ONLY serve http requests comming from server A and deny all other requests.
We are trying to force this by using cors
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(name: "AllowSpecificOrigin", builder =>
{
builder.WithOrigins(new string[0]); // Empty list as a test.
});
});
...
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider serviceProvider)
{
app.UseRouting();
app.UseCors("AllowSpecificOrigin");
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
...
}
But now in one of our controllers, there are two methods. One that requires CORS and one that doesn't.
[ApiController]
[Route("api/Server")]
public sealed class ServerController : ControllerBase
{
[HttpGet]
[Route("ServerCheck")]
[DisableCors]
public IActionResult ServerCheck()
{
return Ok(true);
}
[HttpGet]
[Route("Version")]
[EnableCors("AllowSpecificOrigin")]
public IActionResult Version()
{
return Ok(GetType().Assembly.GetName().Version.ToString());
}
}
If I know issue a request through Postman to both methods, both methods supply an answer. But since we did not allow any origin, how come our request is served in the controller?
Is CORS the wrong way to tackle this issue?
What whould be the best way?
Or is our configuration simply wrong?
Nowhere in our appsettings.json or appsettings.Development.json is there a line 'AllowedHosts'.
Have you considered an IP Address allow list? Or an API Key or some other form of authentication?
It seems as if you are trying to leverage CORS for access control which is not it's purpose. The Mozilla Docs are pretty good, but the short version is that CORS is a way to safely get around the browser same origin restrictions and you should not expect it to work in the same way using Postman or any other non-browser client.
It is also worth considering that every header can be controlled by an attacker and so shouldn't be trusted and any credentials should be transmitted only via https
You could create a custom authorization filter which validates that the host is allowed via an authorization filter
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
public class ValidateDomainAttribute : Attribute, IAuthorizationFilter
{
private IEnumerable<string> AllowedDomains { get; }
public ValidateDomainAttribute(params string[] domains){
AllowedDomains = domains;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
var host = context.HttpContext.Request.Host;
if (!AllowedDomains.Contains(host, StringComparer.OrdinalIgnoreCase))
{
context.Result = new BadRequestObjectResult("Domain is not allowed !");
}
}
}
And to apply this globally for all controllers and actions by registering this attribute in the StartUp class:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers(options =>
{
options.Filters.Add(new ValidateDomainAttribute("your-allowed-domain-1", "your-allowed-domain-2");
)};
// rest of code
}
}
More about filter attributes can be found here: https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-5.0

.NET Core Identity Server dynamic Client app registration and authentication

I have created an Identity Server using .NET Core and IdentityServer4, I have set of APIs and all calls to these APIs must be authenticated but these APIs might be used by third-party applications so clients can be dynamic
Till now example I am finding is set Clients on startup statically like
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients());
}
public class Config
{
public static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
new ApiResource("resourceApi1", "API Application1")
new ApiResource("resourceApi2", "API Application2")
};
}
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientId = "clientApp1",
// no interactive user, use the clientid/secret for authentication
AllowedGrantTypes = GrantTypes.ClientCredentials,
// secret for authentication
ClientSecrets =
{
new Secret("secret".Sha256())
},
AllowedScopes = { "resourceApi1" }
}
};
}
}
Is there a way in IdentityServer implementation to register Client Apps and set dynamically
For Example, If I have APIs
1. resourceApi1
2. resourceApi2
Each third-party APIs should be able to register and we should be able to generate ClientID and Client secret for each with what resources they can access and Identity Server authenticates that ClientID and Client Secret?
There is an option to use a client store. By default identity server uses in memory store for finding clients:
services.AddIdentityServer()
.AddInMemoryClients(Clients)
You can change this and register your own client store
services.AddIdentityServer()
.AddClientStore<MyClientStore>()
and implement the service
class MyClientStore : IClientStore
{
public Task<Client> FindClientByIdAsync(string clientId)
{
}
}
This would solve dynamic lookup of clients. For registration of clients and their management, you would need to implement your own infrastructure.
First of all configure your IdentityServer using EntityFramework, Then you need to some Apis to add Client and ApiResources.
IdentityServer has its own implementation of Stores using EntityFramework.
You can add new Api for this purpose.
[Route("api/[controller]")]
[ApiController]
[AllowAnonymous]
public class DynamicController : ControllerBase
{
private readonly ConfigurationDbContext context;
public DynamicController(ConfigurationDbContext context)
{
this.context = context;
}
[HttpPost]
public async Task<ActionResult> AddApiResource(ApiResource apiResource)
{
context.ApiResources.Add(apiResource);
await context.SaveChangesAsync();
return Ok();
}
[HttpPost]
public async Task<ActionResult> AddClient(Client client)
{
context.Clients.Add(client);
await context.SaveChangesAsync();
return Ok();
}
}

Spring Cloud Zuul Doesn't Relay Access Token

I am trying to use Spring Cloud Zuul as an api/authentication gateway. I have successfully implemented bearer token authorization for my service behind zuul and I successfully have Zuul forwarding to my form login and routing back to my application, but I cannot get Zuul to pass the bearer token to the service.
My Zuul configuration is as follows:
#EnableEurekaClient
#EnableZuulProxy
#SpringBootApplication
#RestController
public class Application { ... }
My service configuration is as follows:
#Profile("oauth")
#Configuration
#EnableResourceServer
#EnableWebSecurity
public static class InternalApiGatewayConfig extends ResourceServerConfigurerAdapter {
When my Angular app tries to access my service through zuul, I get
{"error":"unauthorized","error_description":"Full authentication is required to access this resource"}
I have managed to work around this issue by putting the following code in a ZuulFilter, but it doesn't seem right:
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails)authentication.getDetails();
String tokenValue = details.getTokenValue();
ctx.addZuulRequestHeader("Authorization", "bearer " + tokenValue);
My understanding is that Zuul should automatically send the bearer token value. What am I missing?
So I've figured out the answer to my own question, and it was painfully simple. My project imported spring-security-oauth2. I simply needed to add a dependency on spring-cloud-security as well. With that, I did not have to implement a ZuulFilter at all.
Btw this is the solution that works without spring-cloud-security
#Component
public class TokenRelayFilter extends ZuulFilter {
#Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
#SuppressWarnings("unchecked")
Set<String> headers = (Set<String>) ctx.get("ignoredHeaders");
// JWT tokens should be relayed to the resource servers
headers.remove("authorization");
return null;
}
#Override
public boolean shouldFilter() {
return true;
}
#Override
public String filterType() {
return "pre";
}
#Override
public int filterOrder() {
return 10000;
}
}

asp.net web api json not returning when on server

I've built a web api for my mobile app. I test it on the localhost in browser and it returns json as expected, but whenever I host it on azure and hit the same URL I receive no data, but google network (developer console) in chrome says it has retrieved 408B of data, but no content shows up where the json should be present?
What could I not have configured correctly?
I have gone into the Global.ascx file and added the following line of code
public static void Register(HttpConfiguration config)
{
config.EnableCors();
}
and added the EnableCors attribute on top of the webapi controller class and set the prefix router too.
What am I missing?
Controller code see below for a snippet of it. the database is an EF Code First generated from a database that already existed. Please note.
[EnableCors(origins: "*", headers: "*", methods: "*")]
[RoutePrefix("api/numbers")]
[AllowAnonymous]
public class NumbersController : ApiController
{
private MegaMillions db = new MegaMillions();
[HttpGet]
public HttpResponseMessage Get()
{
return db.Numbers.Take(10).ToList();
}
// GET: api/Numbers
public List<Number> GetNumbers()
{
return db.Numbers.ToList();
}
}
if I can figure out how to get the list working the other CRUD operations will follow suit.
[EnableCors(origins: "*", headers: "*", methods: "*")]
[RoutePrefix("api/values")]
public class ValuesController : ApiController
{
List<Doctor> doctors = new List<Doctor>(){ new Doctor { Id = 1, FirstName = "Carl Finch", LastName = "Finch" }, new Doctor { Id = 2, FirstName = "Carl", LastName = "Finch" } };
// GET api/values/5
[Route("Get")]
public string Get()
{
return "This is my controller response";
}
[Route("GetNumbers")]
public List<Doctor> GetNumbers()
{
return doctors;
}
}
So after adding Routes to each method in the web api it worked just fine!
So remember declaring the RouterPrefix is NOT ENOUGH to get data, each method in a web api has to have a proper Router attribute.

Resources