Disable CORS in ASP.NET MVC framework - asp.net-mvc

I developped an ASP.NET MVC web site. In this site I have some url, for example, site.com/url/ where I can send post request to and get some response.
How can I disable getting results from this url for requests from different domains and allow getting results only for requests from domain site.com?
I read about CORS but I don't know whether it can be used in my case.

builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
...
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins);
app.UseAuthorization();
link to article

Related

Getting 401 Unauthorized with MVC Pages while Identity Razor pages work as expected

Background
I am doing a POC to find out if Angular, Razor and MVC pages work seamlessly in a web application. I started with Visual Studio template named "ASP.NET Core with Angular". I have selected "Individual Accounts" to include default authentication functionality. This creates an Angular app with a secure web API endpoint (WeatherForecast) and provides basic user registration, login, logout, user profile pages etc features built in. So far all works well, when I try to fetch data from the protected API (WeatherForecast) I get redirected to the Identiy/Account/Login razor page where I can login and then get redirected back to Angular and I can see that data is returned and grid is populated. Till this point everything works fine.
The Problem
I added a DemoController class with a basic "Hello World" HTML view. When I try to access this new page with /demo, it works as expected. However, when I apply [Authorize] attribute to the controller, I get 401 Unauthorized. I checked on server side that User.IsAuthenticated property is set to false despite having successfully logged in before. Now interesting observation is that the user profile page (which is protected and works only if there an active login) works fine.
Please note that all API calls issues from Angular use JWT bearer token and work fine. When I try to access user profile page, it does NOT use JWT, it uses cookies to authenticate. The GET request to /demo page also has all these cookies in headers, still it is met with 401.
I spent a lot of time going thru articles, searching web with no success. The closing thing we found is this : ASP.NET Core 5.0 JWT authentication is throws 401 code
But that didn't help either.
The project is created using Visual Studio 2022, .net core 6.0. Here is the Program.cs file for your reference:
using CoreAngular.Data;
using CoreAngular.Models;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
builder.Services.AddAuthentication()
.AddIdentityServerJwt();
builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
}
else
{
// 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.UseIdentityServer();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
app.MapRazorPages();
app.MapFallbackToFile("index.html"); ;
app.Run();
This has been answered here: https://stackoverflow.com/a/62090053/3317709
It turned out that using IdentityServer extension methods add a policy scheme such that only /Identity pages have cookie authentication. The rest default to JWT.
We can customize this by adding our own policy like so:
builder.Services.AddAuthentication()
.AddIdentityServerJwt()
.AddPolicyScheme("ApplicationDefinedAuthentication", null, options =>
{
options.ForwardDefaultSelector = (context) =>
{
if (context.Request.Path.StartsWithSegments(new PathString("/Identity"), StringComparison.OrdinalIgnoreCase) ||
context.Request.Path.StartsWithSegments(new PathString("/demo"), StringComparison.OrdinalIgnoreCase))
return IdentityConstants.ApplicationScheme;
else
return IdentityServerJwtConstants.IdentityServerJwtBearerScheme;
};
});
// Use own policy scheme instead of default policy scheme that was set in method AddIdentityServerJwt
builder.Services.Configure<AuthenticationOptions>(options => options.DefaultScheme = "ApplicationDefinedAuthentication");

Preventing CSRF in Angular 2 / ASP.NET MVC application

I am working on a sample SPA using ASP.NET MVC for back end and Angular 2 for front end.
I followed below steps to prevent cross site request forgery attacks in my application
Since ASP.NET MVC sends a cookie with name "__RequestVerificationToken", and expects a header with name "__RequestVerificationToken" in the HTTP request to prevent CSRF , I have added below code in my angular module
{provide: XSRFStrategy, useFactory: xsrfFactory}
where xsrfFactory is below function
export function xsrfFactory() {
return new CookieXSRFStrategy('__RequestVerificationToken', '__RequestVerificationToken');
}
And below is the controller action code with "[ValidateAntiForgeryToken]" attribute , to which an AJAX call will be made using Http service of Angular 2.
[CustomAuth]
[ValidateAntiForgeryToken]
public ActionResult GetAuthors()
{
List<BookStoreAdmin.ViewModels.Author> authors = BookStoreAdmin.BAL.Author.GetAuthors();
BookStoreAdmin.ViewModels.Response<List<BookStoreAdmin.ViewModels.Author>> response = new Response<List<ViewModels.Author>>();
response.success = true;
response.errorMessage = null;
response.data = authors;
return Json(response, JsonRequestBehavior.AllowGet);
}
Below is the code which makes the AJAX call .
loadAuthors(): Observable<AuthorModel[]> {
return this.http.get('http://localhost:57599/author/GetAuthors')
.map((data) => data.json());
}
When my application makes an AJAX call using Http angular service , I was expecting it to have request header with name "__RequestVerificationToken" , but this
header is missing , any idea what could be the reason ?
Please let me know if more information needs to be provided ?
I can't see if you are passing a header in angular2 http call.
You can use RequestOptions API which allows you to add header. After adding header when you make request, ValidateAntiForgeryToken should be able to receive sent header.
Read more of RequestOptions here :
https://angular.io/docs/ts/latest/api/http/index/RequestOptions-class.html
Late answer but might be useful for someone.
I think the header is not set because this is a GET request. Though this is Angular 2, the angular 4 security docs might be relevant here as they state that
By default, an interceptor sends this cookie on all mutating requests (POST, etc.) to relative URLs but not on GET/HEAD requests or on requests with an absolute URL.
In order to explicitly include this header as #micronyks states in his answer you can use the RequestOptions API. Here's a code sample
var headers = new Headers();
headers.append('__RequestVerificationToken', <token>);
return this.http.get(url, {headers: headers});

OAuth token expiration in MVC6 app

So I have an MVC6 app that includes an identity server (using ThinkTecture's IdentityServer3) and an MVC6 web services application.
In the web services application I am using this code in Startup:
app.UseOAuthBearerAuthentication(options =>
{
options.Authority = "http://localhost:6418/identity";
options.AutomaticAuthentication = true;
options.Audience = "http://localhost:6418/identity/resources";
});
Then I have a controller with an action that has the Authorize attribute.
I have a JavaScript application that authenticates with the identity server, and then uses the provided JWT token to access the web services action.
This works, and I can only access the action with a valid token.
The problem comes when the JWT has expired. What I'm getting is what appears to be a verbose ASP.NET 500 error page, that returns exception information for the following exception:
System.IdentityModel.Tokens.SecurityTokenExpiredException
IDX10223: Lifetime validation failed. The token is expired.
I am fairly new to OAuth and securing Web APIs in general, so I may be way off base, but a 500 error doesn't seem appropriate to me for an expired token. It's definitely not friendly for a web service client.
Is this the expected behavior, and if not, is there something I need to do to get a more appropriate response?
Edit: this bug was fixed in ASP.NET Core RC2 and the workaround described in this answer is no longer needed.
Note: this workaround won't work on ASP.NET 5 RC1, due to this other bug. You can either migrate to the RC2 nightly builds or create a custom middleware that catches the exceptions thrown by the JWT bearer middleware and returns a 401 response:
app.Use(next => async context => {
try {
await next(context);
}
catch {
// If the headers have already been sent, you can't replace the status code.
// In this case, throw an exception to close the connection.
if (context.Response.HasStarted) {
throw;
}
context.Response.StatusCode = 401;
}
});
Sadly, that's how the JWT/OAuth2 bearer middleware (managed by MSFT) currently works by default, but it should be eventually fixed. You can see this GitHub ticket for more information: https://github.com/aspnet/Security/issues/411
Luckily, you can "easily" work around that by using the AuthenticationFailed notification:
app.UseOAuthBearerAuthentication(options => {
options.Notifications = new OAuthBearerAuthenticationNotifications {
AuthenticationFailed = notification => {
notification.HandleResponse();
return Task.FromResult<object>(null);
}
};
});

Implementing Message Handlers in MVC 6

I have current API (web api 2) project that has a number of message handlers in use, some run for every request (checking to make sure the request is https and the request is coming from an authorised client) and some that run on specific routes (checking for the existence of a security token).
Now my question is how do I replicate this functionality in MVC 6, my current understanding is that it should be done through the use of middleware but I've not found an example that allows me to inspect the headers of the incoming request and should they not be what they are supposed to be return the appropriate http response code.
Middleware is definitely the right option to solve what you're looking to solve. I wrote a decent explanation about using/writing middlware here: ASP.NET MVC 6 handling errors based on HTTP status code
To specifically answer how to inspect headers here's an example:
public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.Use(async (context, next) =>
{
if (!string.Equals(context.Request.Headers["myheader"], "somevalue", StringComparison.Ordinal))
{
context.Response.StatusCode = 400;
await context.Response.WriteAsync("Invalid headers");
}
else
{
await next();
}
});
}
}

Jquery AJAX (json) cross domain request and ASP.NET MVC

Seemed to me to be a beaten theme, but i couldn't find the answer. =(
I make jquery ajax requst to
localhost:666 from localhost:555 application
$.ajax({
url: "http://localhost:666/request",
dataType: 'json',
timeout: 5000,
success:...
i've got in chrome:
XMLHttpRequest cannot load http://localhost:666/request. Origin http://localhost:555 is not allowed by Access-Control-Allow-Origin.
What is the solution of the problem?
You can initiate cross-domain request in your webpage by creating either XMLHttpRequest object or XDomainRequest object. End user's web-browser will request data from the domain's server by sending an "Origin" header with the value of origin. If server responds with an "Access-Control-Allow-Origin: * | Origin" then we are permitted to access data; otherwise response will be unauthorized request.
protected void Application_BeginRequest(object sender, EventArgs e)
{
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");
// HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "http://AllowedDomain.com");
}
An article here: Cross-Origin requests and ASP.NET MVC
ajax calls are confined to parent domain only. for this a site on localhost:666 can not open ajax connection to localhost:555 since they belongs to different domain (or origin)
you need to try jsonp: http://www.google.com/search?q=jsonp
Try using dataType: 'jsonp', or $.getJSON function.

Resources