Custom Authorization Filter in .NET Core API - asp.net-core

I want to authorize users before accessing any data using my core api, so I tried is using JWT authentication.
I have successfully generated token while signing in user using api and saved that token on client side in session, now whenever user wants to access any data using api, I'll send that token in header to api and I want to validate that JWT token using custom authorization filter. I have created custom authorization filter and applied it on my GetMenu api and I'm able to validate that token successfully but after token validation in authorization filter it is not hitting it on my GetMenu api.
Here is my AccountController code:
[Filters.Authorization]
[AllowAnonymous]
[HttpPost]
[Route("GetMenu")]
public IActionResult GetMenu(string clientid, int rolecode, string repcode)
{
//further process
}
Here is my Filters.Authorization code:
public class Authorization: AuthorizeAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext filterContext)
{
if (!ValidateToken(filterContext.HttpContext.Request.Headers["token"]))
{
filterContext.Result = new UnauthorizedResult();
}
}
}
I have breakpoints on OnAuthorization method and on GetMenu api.
I'm calling my GetMenu api through postman to test, it is successfully hitting it on OnAuthorization method in Filters.Authorization and validating my JWT Token and displays Status Code: 200 in postman but after successful token validation it should hit on GetMenu api for further data processing but it is not hitting.
What can be the issue? what am i missing? please help.

You should not set the filterContext.Result if the request is successfully authorize.
//
// Summary:
// A context for authorization filters i.e. Microsoft.AspNetCore.Mvc.Filters.IAuthorizationFilter
// and Microsoft.AspNetCore.Mvc.Filters.IAsyncAuthorizationFilter implementations.
public class AuthorizationFilterContext : FilterContext
{
//
// Summary:
// Gets or sets the result of the request. Setting Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext.Result
// to a non-null value inside an authorization filter will short-circuit the remainder
// of the filter pipeline.
public virtual IActionResult Result { get; set; }
}
You only need to set Result when it's failed.
public class Authorization: AuthorizeAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext filterContext)
{
if (!ValidateToken(filterContext.HttpContext.Request.Headers["token"]))
{
filterContext.Result = new UnauthorizedResult();
}
}
}

Related

Custom attribute validation in ASP.NET Core is running before JWT token is processed

asp.net core authorization
I am trying to use a custom authorization attribute to have finer control over my controller actions like this (somewhat similar to How do you create a custom AuthorizeAttribute in ASP.NET Core?)
[MyCustomAuth(Permissions="Products/Read")]
public IActionResult SomeMethod()
{
.....
}
public class MyCustomAuthAttribute : AuthorizeAttribute, IAuthorizationFilter
{
public string Permissions { get; set; } //Permission string to get from controller
public void OnAuthorization(AuthorizationFilterContext context)
{
//
//read jwttoken
//and process permissions string
//to decide if user can run controller method
//
..
}
}
Unfortunately the JWT authorization handler that is built into ASP.NET core (configured in startup.cs) is run only AFTER this custom attribute is code is run so I can't seem to access the JWT token and THEN process the custom auth parameters.
Is there anyway to force the JWT token to be processed first and then do an extra validation using the custom attribute?
I think I found a solution...it seems to work..but could someone please confirm this is the right way?
Just implement IOrderedFilter interface and set Order to a high number. This means JWT authentication will be called first and then your custom authorization filter.
public class MyCustomAuthAttribute : AuthorizeAttribute, IAuthorizationFilter
{
...
public int Order => 9999;
...
}

How to implement custom ValidateAntiforgeryTokenAuthorizationFilter in ASP.NET Core 3.1

I'd like to implement a filter that skips validation of an antiforgery token when an auth token authentication (Bearer) is used.
In the ASP.NET Core 2.2 the ValidateAntiforgeryTokenAuthorizationFilter and AutoValidateAntiforgeryTokenAuthorizationFilter were public (even though living in the Microsoft.AspNetCore.Mvc.ViewFeatures.Internal namespace), so I was able to just inherit from the latter and override the ShouldValidate method easily.
In the ASP.NET Core 3.0 they became internal, so it's not possible to just inherit from them. I can just copy-paste the code, but it's not the ideal solution obviously.
I was following the Prevent Cross-Site Request Forgery (XSRF/CSRF) attacks in ASP.NET Core article from MSDN, but it doesn't really mention anything relevant to my scenario.
Normally you can use [IgnoreAntiforgeryToken] attribute if you can determine at compile-time that the csrf token should be ignored. If you want such an ability at run-time, you could create a custom FilterProvider that will provide an IAntiforgeryPolicy if there's a Authroization: Bearer json-web-token header.
For example, we can create a custom AutoSkipAntiforgeryFilterProvider as below:
public class AutoSkipAntiforgeryFilterProvider: IFilterProvider
{
private const string BEARER_STRING = "Bearer";
public int Order => 999;
public void OnProvidersExecuted(FilterProviderContext context) { }
public void OnProvidersExecuting(FilterProviderContext context)
{
if (context == null) { throw new ArgumentNullException(nameof(context)); }
if (context.ActionContext.ActionDescriptor.FilterDescriptors != null)
{
var headers = context.ActionContext.HttpContext.Request.Headers;
if (headers.ContainsKey("Authorization"))
{
var header = headers["Authorization"].FirstOrDefault();
if(header.StartsWith(BEARER_STRING,StringComparison.OrdinalIgnoreCase))
{
var FilterDescriptor = new FilterDescriptor(SkipAntiforgeryPolicy.Instance, FilterScope.Last);
var filterItem = new FilterItem( FilterDescriptor,SkipAntiforgeryPolicy.Instance);
context.Results.Add(filterItem);
}
}
}
}
// a dummy IAntiforgeryPolicy
class SkipAntiforgeryPolicy : IAntiforgeryPolicy, IAsyncAuthorizationFilter
{
// a singleton
public static SkipAntiforgeryPolicy Instance = new SkipAntiforgeryPolicy();
public Task OnAuthorizationAsync(AuthorizationFilterContext context) => Task.CompletedTask;
}
}
And register this filter provider in Startup :
services.TryAddEnumerable( ServiceDescriptor.Singleton<IFilterProvider, AutoSkipAntiforgeryFilterProvider>());
Now it will bypass the AntiForgery even there's a [ValidateAntiForgeryToken]attribute.
[Demo]
Assume we have an action method annotated with [ValidateAntiForgeryToken]:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Name")] XModel xModel)
{
....
}
Normally, it will protect this method with CSRF token. But if you send a request like:
POST /XModels/Create HTTP/1.1
Authorization: Bearer Xyz
Content-Type: application/x-www-form-urlencoded
...
it won't validate the csrf token.

AntiforgeryToken validation failed

I've ASP CORE 2.1 application that works in conjunction with angular SPA client.
I protected my application as stated here.
But the problem is that I constantly receive 400 Bad Request right after I login to the system. As per my point of view that is what happened in the system:
First request to the app -> returns AntiForgery CookieToken and
RequestToken (important note that user isn't authenticated yet)
User logins to the system -> AntiForgery validation passed,
authentication cookies sent to the client.
User requests any
other endpoint, but since the AntiforgeryTokenSet was issued for
non-authenticated user, he gets 400 Bad Request.
It is obvious, that after login we need to reissue AntiforgeryTokenSet but I've no idea where and how. I've tried to issue token in the Result Filter but with no luck.
public class SPAAntiforgeryCookieResultFilter : ResultFilterAttribute
{
private readonly IAntiforgery _antiforgery;
public SPAAntiforgeryCookieResultFilter(IAntiforgery antiforgery)
{
_antiforgery = antiforgery;
}
public override void OnResultExecuting(ResultExecutingContext context)
{
Action assignAntiForgery = () =>
{
var tokens = _antiforgery.GetAndStoreTokens(context.HttpContext);
context.HttpContext.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken, new CookieOptions() { HttpOnly = false });
};
if (context.Result is ViewResult)
{
assignAntiForgery();
}
else if (string.Equals(context.ActionDescriptor.ActionName, nameof(AccountController.Login), StringComparison.OrdinalIgnoreCase))
{
assignAntiForgery();
}
}
}
It seems that ResultExecutingContext does't know about authenticated user and issues token still for anonymous user.
So, how we can refresh antiforgery RequestToken token right after login for authenticated user?
For now I find out the way how to get around the issue.
Previously, I returned Ok() after successful login, but now I do RedirectToAction() and my OnResultExecuting filter was slightly changed in order to refresh token after RedirectToAction happend.
public class SPAAntiforgeryCookieResultFilter : ResultFilterAttribute
{
private readonly IAntiforgery _antiforgery;
public SPAAntiforgeryCookieResultFilter(IAntiforgery antiforgery)
{
_antiforgery = antiforgery;
}
public override void OnResultExecuting(ResultExecutingContext context)
{
Action assignAntiForgery = () =>
{
var tokens = _antiforgery.GetAndStoreTokens(context.HttpContext);
context.HttpContext.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken, new CookieOptions() { HttpOnly = false });
};
if (context.Result is ViewResult)
{
assignAntiForgery();
}
// Here we check whether our redirect action is executed. Update AFT if it is
else if (string.Equals(context.ActionDescriptor.RouteValues["action"], nameof(AccountController.RefreshAntiForgeryAfterLogin), StringComparison.OrdinalIgnoreCase))
{
assignAntiForgery();
}
}
}
This dirty hack works in the next way:
First request to the app -> returns AntiForgery CookieToken and RequestToken (important note that user isn't authenticated yet)
User press login to the system -> AntiForgery validation passed, authentication cookies set up -> User get response with authentication cookies and status code 302 (redirect) -> User reach out our RedirectUserToAction method with context that has authentication info (User.IsAuthenticated == true)

Intercepting an encrypted login token in a request

I am working on an MVC site that has some pages that need authentication and others that don't. This is determined using the Authorize and AllowAnonymous attributes in a pretty standard way. If they try to access something restricted they get redirected to the login page.
I'm now wanting to add the functionality to automatically log them in using an encrypted token passed in the querystring (the link will be in emails sent out). So the workflow I want now is that if a request goes to a page that is restricted and there is a login token in the querystring I want it to use that token to log in. If it logs in successfully then I want it to run the original page requested with the new logged in context. If it fails to log in then it will redirect to a custom error page.
My question is where would I need to insert this logic into the site?
I have seen some suggestions on subclassing the Authorize attribute and overriding some of the methods but I'm not 100% sure how to go about this (eg what I would override and what I'd do in those overridden methods.
I've also had a look at putting the logic at a controller level but I am led to understand that the authorize attribute would redirect it away from the controller before any code in the controller itself was run.
It would be better to write a custom authorization attribute that will entirely replace the default functionality and check for the query string parameter and if present, decrypt it and authenticate the user. If you are using FormsAuthentication that would be to call the FormsAuthentication.SetAuthCookie method. Something along the lines of:
public class TokenAuthorizeAttribute : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
string token = filterContext.HttpContext.Request["token"];
IPrincipal user = this.GetUserFromToken(token);
if (user == null)
{
this.HandleUnAuthorizedRequest(filterContext);
}
else
{
FormsAuthentication.SetAuthCookie(user.Identity.Name, false);
filterContext.HttpContext.User = user;
}
}
private IPrincipal GetUserFromToken(string token)
{
// Here you could put your custom logic to decrypt the token and
// extract the associated user from it
throw new NotImplementedException();
}
private void HandleUnAuthorizedRequest(AuthorizationContext filterContext)
{
filterContext.Result = new ViewResult
{
ViewName = "~/Views/Shared/CustomError.cshtml",
};
}
}
and then you could decorate your action with this attribute:
[TokenAuthorize]
public ActionResult ProcessEmail(string returnUrl)
{
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
return RedirectToAction("Index", "Home");
}

Where do I handle custom authentication and authorization in webapi?

NOTE: I have many questions littered in the code comments below. I need answers to those as well.
I have read (among many others) the following articles:
http://blogs.msdn.com/b/hongmeig1/archive/2012/05/11/how-to-write-a-custom-parameter-binding-to-construct-an-object-either-from-body-or-from-uri-s-query.aspx
http://blogs.msdn.com/b/jmstall/archive/2012/05/11/webapi-parameter-binding-under-the-hood.aspx
I would like for my web api to have authentication sent in the header using the Authorization header. I would like this header to be populated into a c# class called AuthenticationToken. Then when I am doing parameter binding I would like to retreive this previously created AuthenticationToken object and pass it on to my controller action. For example, if I have the following controller
public class MyServiceController : ApiController {
readonly ISecurityService _security;
readonly IMyService _myService;
// constructor values are injected
public MyServiceController(ISecurityService security, IMyService myService) {
_security = security;
_myService = myService;
}
public SomeData GetASpecificItem(AuthenticationToken token, int id) {
if (_security.IsAuthorized(token, Permissions.Read)) {
return myService.DoStuffToGetSomeData(token);
} else {
var msg = new HttpResponseMessage(HttpStatusCode.Forbidden);
throw new HttpResponseException(msg);
}
}
}
and the following parameter binding class
public class AuthenticationTokenParameterBinding
: HttpParameterBinding { // do I need to inherit from a different class?
public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider,
HttpActionContext actionContext,
CancellationToken cancellationToken) {
try {
AuthenticationToken token; // UPDATED: how can i get this from the data
// available from inside this method?
SetValue(actionContext, token);
// is this the correct task to return on successfull parameter binding?
return base.ExecuteBindingAsyn(metadataProvider, actionContext, cancellationToken);
} catch {
return Task<HttpResponseMessage>.Factory.StartNew(() => {
var hpm = new HttpResponseMessage(HttpStatusCode.Unauthorized);
hpm.Headers.Add("WWW-Authenticate","MyCustomScheme");
return hpm;
});
}
}
}
If these two are implemented correctly, then the controller will automatically get the AuthenticationToken instance that was created during authorization.
I do not know where to authenticate ahead of this process. Nor do I know how to pass an object between authentication and authorization.
UPDATE:
I can't use a custom AuthorizeAttribute because authorization may be against an object:
public SaveResponse Save(AuthenticationToken user, SomeObjectThatNeedsToBeSaved obj) {
// NOTE: permissions are checked between the object and the user, not a role
if (_security.IsAuthorized(user, obj, Permission.Modify, Permission.Create)) {
// NOTE: other permissions we don't know about may need to be checked in the service call
return new SaveResponse {
Success = ISomeService.Save(user, obj); // bool return value
}
} else {
// return 403 Forbidden }
}
I need to pass the token to the controller action, but I also need to authenticate the token before it gets passed to the controller. Since all of this is not necessarily role based, I don't see how I can authenticate from inside of a custom AuthorizeAttribute
I have used a custom AuthorizeAttribute to handle both authentication and authorization for Web API. This attribute works as a filter and will process the request before it gets to your Web API method. In the overridden OnAuthorize method you can return HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized) if authentication fails and HttpResponseMessage(System.Net.HttpStatusCode.Forbidden) if authorization fails so that the client can distinguish between both types of errors. In addition to the custom AuthorizeAttribute I implemented a custom MembershipProvider and RoleProvider to handle my specific security requirements and custom database schema.
I use basic authentication to pass the credentials for authorization. This puts the credentials in the header. To do this is pretty straight forward by using the beforeSend event handler of the JQuery ajax function. Here is an example of how to do this.
getAuthorizationHeader = function (username, password) {
var authType;
var up = $.base64.encode(username + ":" + password);
authType = "Basic " + up;
};
return authType;
};
$.ajax({
url: _url,
data: _data,
type: _type,
beforeSend: function (xhr) {
xhr.setRequestHeader("Authorization", getAuthorizationHeader(username, password));
},
success: ajaxSuccessHandler,
error: ajaxErrHandler
});
This encodes the username/password that is sent in the header. Note that this is not enough security to rely on just the encoding as it is easy to decode. You still want to use HTTPS/SSL to make sure the information sent over the wire is secure.
On the Web API side you can make a custom AuthorizeAttribute that gets the credentials from the header, decodes them, and performs your authorization process. There is a separate AuthorizeAttribute used by the Web API as opposed to the controller. Be sure to use System.Web.Http.AuthorizeAttribute as your base class when creating your custom AuthorizeAttribute. They have different behaviors. The one for the controller will want to redirect to the logon page whereas the one for the Web API returns an HTTP code indicating success or failure. I return an HTTP code of Forbidden if authorization fails to distinguish a failure due to authorization as opposed to authentication so the client can react accordingly.
Here is an example method for getting the credentials from the header that can be used in the custom AuthorizeAttribute.
private bool GetUserNameAndPassword(HttpActionContext actionContext, out string username, out string password)
{
bool gotIt = false;
username = string.Empty;
password = string.Empty;
IEnumerable<string> headerVals;
if (actionContext.Request.Headers.TryGetValues("Authorization", out headerVals))
{
try
{
string authHeader = headerVals.FirstOrDefault();
char[] delims = { ' ' };
string[] authHeaderTokens = authHeader.Split(new char[] { ' ' });
if (authHeaderTokens[0].Contains("Basic"))
{
string decodedStr = SecurityHelper.DecodeFrom64(authHeaderTokens[1]);
string[] unpw = decodedStr.Split(new char[] { ':' });
username = unpw[0];
password = unpw[1];
}
gotIt = true;
}
catch { gotIt = false; }
}
return gotIt;
}
And here is the code for decoding the header data that is used in this method.
public static string DecodeFrom64(string encodedData)
{
byte[] encodedDataAsBytes
= System.Convert.FromBase64String(encodedData);
string returnValue =
System.Text.Encoding.ASCII.GetString(encodedDataAsBytes);
return returnValue;
}
Once you have the username and password you can perform your authorization process and return the appropriate HTTP code to the client for handling.
You could perform the a similar process with your custom token, or you can leverage the cookie that is passed back and forth if you do not want to keep the password/username stored in the client.