Implementing Message Handlers in MVC 6 - middleware

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

Related

How to add CORS middleware in shelf with router?

I am new to server side programming with dart. I made a simple API server with a number of get routes. I am handling this as follow,
Router router = Router();
router.get('/', checkSTATUS);
router.get('/login/<user>/<pass>', (Request request, String user, String pass) async {
id = 0;
// stuff
return Response.ok(json.encode({"status":"found","id":id}));
});
router.get('/update', (Request request) async {
//stuff
return Response.ok(json.encode({"status": "updated", "data": updated}));
});
//for any other requests
router.all('/<ignored|.*>', (Request request) {
return Response.notFound(json.encode('Page not found'));
});
final server = await serve(
router,
InternetAddress.anyIPv4,
8080,
);
I can access these routes using postman but making requests using flutter web results in error. I searched and found out that this may be CORS related. But how do I add the CORS headers without disrupting the entire code.
Please refer this document. https://pub.dev/packages/shelf_cors_headers.
Install this package

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

Asp.net Core, JWT, and CORS Headers

I'm having an issue getting an appropriate Access-Control-Allow-Origin header back from the server when I have both JWT Bearer Authentication and CORS enabled on the same service.
When I remove UseJwtBearerAuthentication from the configuration, everything works.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("AllowAllOrigins", builder =>
{
builder.AllowAnyOrigin();
builder.AllowAnyHeader();
builder.AllowAnyMethod();
builder.AllowCredentials();
});
});
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseJwtBearerAuthentication(options =>
{
options.AutomaticAuthenticate = true;
options.RequireHttpsMetadata = false;
options.Audience = "c2cf422a-a432-2038-b183-cda64e16239e";
options.Authority = "domain.com";
});
app.UseCors("AllowAllOrigins");
app.UseIISPlatformHandler();
app.UseMvc();
}
I've tried to change the ordering for configuration, but nothing seems to work. I also tried adding [EnableCors("AllowAllOrigins")] to the controller I'm calling.
I've changed the config order based on the recommendation in the comments and identified the property causing the issue:
app.UseIISPlatformHandler();
app.UseCors("AllowAllOrigins");
app.UseJwtBearerAuthentication(options =>
{
options.AutomaticAuthenticate = true;
options.RequireHttpsMetadata = false;
options.Audience = "c8cf662a-ac73-4050-b285-cda90e22992e";
options.Authority = "iwdwk.com";
});
app.UseMvc();
In the code above, the line below seems to be causing the issue:
options.AutomaticAuthenticate = true;
Unfortunately, I need to have that enabled so I can pass the JWT token through for authorization... Unless there is another way to do this?
I guess, the OPTIONS call from your browser are getting rejected by authentication since they may not contain the bearer token. I am not sure but there must be a way to skip authentication for OPTIONS call when calling UseJwtBearerAuthentication. If you wish to confirm this before then try calling your endpoint from postman, since it skips the OPTIONS call and see if you get the access control headers for actual GET/POST call
If the server throws and exception they clear the headers and so you'll only see the CORS error. The way I used to see what was actually going on was to first get the Bearer Token out of one of my failed requests using the Chrome dev tools.
Next I copied that token and pasted into Postman as the value for an Authorization header in a request. When I did that I finally received that nice developer exception page that told me exactly what I was doing wrong. Which was that I was using the ClientId GUID Azure AD gives out instead of the App URI.
So if you are using Azure AD then your JWT options should look like:
app.UseJwtBearerAuthentication(options =>
{
options.AutomaticAuthenticate = true;
options.AutomaticChallenge = true;
options.Authority = "https://login.microsoftonline.com/yourtenant.onmicrosoft.com"
options.Audience = "https://yourtenant.onmicrosoft.com/AppURI"
});
I was having a similar issue. When making a POST/GET and the browser fire a Preflight with OPTIONS request, my angular app wasn't able to understand that was a 401 response. It was returning as 0 - Unknown error.
My problem was the order of my services. All I had to do was in Configure method inside Startup.cs file, change the order. I added first the CORS config and then the JWT middleware and then UseAuthentication
Realize this question is old but it's likely due to the fact that you're performing authentication across CORS with both AllowCredentials=true and AllowAnyOrigin=true. This isn't allowed. Try specifying a specific origin.

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

Passing Authentication Token with Breeze query

My server side breeze api calls require me to validate the token value provided with each call before returning any data. To achieve this, i am passing TokenId with each Entity Query using withParameters function of breeze Entity Query and specify the parameter on my server side controller action as illustrated below.
Following is how i am doing it right now:
Client Side
function GetCustomers(){
return breeze.EntityQuery.from('Customers')
.withParameters({ TokenId: 'token value' })
.using(entityManager).execute()
}
Server Side
[HttpGet]
public IQueryable<Customer> Customers(string TokenId)
{
//server side logic
}
This for some reason looks to me can be simplified using some configuration on Breeze Entity Manager which automatically adds the parameter value to every query sent from client. And also, on server side, i don't think its necessary to have tokenId parameter defined on each controller method. This should be easy to solve with Action Filters on the server side.
Can someone point me in right direction if it is possible and how to do this?
Thanks
You should always pass custom authentication information, such as tokens, in the headers.
If you can pass it as a custom header you can search for 'define custom headers breeze.js' to see how that can be done.
A good example -
http://breeze.github.io/doc-js/server-ajaxadapter.html
Also keep in mind that if you put the token in the query string that is persisted by most of the servers that your call is crossing over, making it much easier to find tokens for your application if an evil-doer were to download the logs from that server.
Also keep in mind that if you are using cross-domain requests (CORS) you will need to enable the custom header on the server side that is receiving the call.
To give an example of the answer above:
// get the current default Breeze AJAX adapter
var ajaxAdapter: any = breeze.config.getAdapterInstance('ajax');
// set fixed headers
ajaxAdapter.defaultSettings = {
headers: {
"Bearer": this.bearerToken
}
};
Note also that if you are using ASP.net Identity (which includes a cookie based authentication), you must include this code so that the asp.net pipeline does NOT use that cookie:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Filters.Add(new Ask.Filters.RequireHttpsAttribute());
//This forces http header authentication which is required for web api calls.
config.SuppressDefaultHostAuthentication();
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
Note also that you should use only https, with this filter:
public class RequireHttpsAttribute : AuthorizationFilterAttribute
{
public int Port { get; set; }
public RequireHttpsAttribute()
{
Port = 443;
}
public override void OnAuthorization(HttpActionContext actionContext)
{
var request = actionContext.Request;
if (request.RequestUri.Scheme != Uri.UriSchemeHttps)
{
var response = new HttpResponseMessage();
if (request.Method == HttpMethod.Get || request.Method == HttpMethod.Head)
{
var uri = new UriBuilder(request.RequestUri);
uri.Scheme = Uri.UriSchemeHttps;
uri.Port = this.Port;
response.StatusCode = HttpStatusCode.Found;
response.Headers.Location = uri.Uri;
}
else
{
response.StatusCode = HttpStatusCode.Forbidden;
}
actionContext.Response = response;
}
else
{
base.OnAuthorization(actionContext);
}
}
}
And finally, make sure you use the [Authorize] and [HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)] attribute on your controllers.
See the Full Sample and article on asp.net
I suggest using oAuth tokens in the header. I am currently using Auth0, but there are many others including Google Firebase. BreezeJS has been expanded such that it will accept AuthHttp as it's HTTP client for all calls. AuthHttp will automatically add a bearer token to the header of every call to the server.
One the server side it's a matter of adding the proper oAuth client lib (via Nuget) which automatically parses out the bearer token, validates it against the oAuth server, and makes claims available for use within your API call.
For example, in my implementation my api code will receive a claim that contains the users id, which I can then validate against my db and use to filter all actions. This keeps user B from loading user A's data by directly calling the api in a browser.
For example, all API calls are first validated by the oAuth subsystem. Once my api code is executed I know the caller has been validated and I use the passed claims (user id) to access only the calling users data - REGARDLESS what was actually requested by the API call parameters (which can be forged as easily as editing a URL string).
A simple example of this would be exposing your UserSet in the API. If you don't filter by the user claim id you must realize that every user in your system will be accessible by constructing a simple URL in a browser.
Here I filter out and return only the user record identified by the user id in the auth claim. Note I use #if AUTH simply for testing as PROD will always have AUTH turned on.
#if AUTH
[Authorize]
#endif
[HttpGet]
[EnableBreezeQuery(MaxExpansionDepth = 5)]
public IQueryable<User> UserSet()
{
#if AUTH
Guid guid = userGuid();
return _efContext.Context.UserSet.Where(x => x.active && x.guid == guid);
#else
return _efContext.Context.UserSet;
#endif
}
All of this sounds complicated, but it really is quite easy to implement.
Thanks,
Mike

Resources