AllowAnonymous attribute is not working in .net core api 2.2. Please consider (.NetCore) and version(2.2) before suggesting duplicate or answer - asp.net-core-mvc

I am working in .net core api 2.2 where I am using authorization filter. I am just checking bearer token in authorization tag in header and if bearer token is already there then user action can be called. But some action I wanna exclude from authorization part. I am using AllAnonymous attribute on specific action but calling on same anonymous method the authorization filter is being called. The code of filter is given below :
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Filters;
public class ApiAuthorizeFilter : AuthorizeAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
string token = context.HttpContext.GetToken();
if (string.IsNullOrEmpty(token))
{
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
}
else
{
string realmId = context.HttpContext.GetRealm();
if (string.IsNullOrEmpty(realmId))
{
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
}
}
}
}
As per I checked the some solution per this is not getting resolved. Please share any solution regarding .net core API version 2.2 .

You can check the AllowAnonymous attribute inside OnAuthorization method :
// Allow Anonymous skips all authorization
if (context.Filters.Any(item => item is IAllowAnonymousFilter))
{
return;
}
Base on your codes :
public class ApiAuthorizeFilter : AuthorizeAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
// Allow Anonymous skips all authorization
if (context.Filters.Any(item => item is IAllowAnonymousFilter))
{
return;
}
string token = context.HttpContext.GetToken();
if (string.IsNullOrEmpty(token))
{
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
}
else
{
string realmId = context.HttpContext.GetRealm();
if (string.IsNullOrEmpty(realmId))
{
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
}
}
}
}

When doing endpoint routing, MVC does not add AllowAnonymousFilters for AllowAnonymousAttributes that
were discovered on controllers and actions. To maintain compat with 2.x,
we'll check for the presence of IAllowAnonymous in endpoint metadata.
var endpoint = context.HttpContext.GetEndpoint();
if (endpoint?.Metadata?.GetMetadata<IAllowAnonymous>() != null)
{
return true;
}

Related

Custom Async Action Filter for Web API 2

I have a web api to consume the data coming from android mobile. This web api will consume the multi part file from along with the form data the web api request. I followed this article to archive.
[CustAuthAsync]
public async Task<HttpResponseMessage> SaveEHSInspectionData()
{
try
{
string root = HttpContext.Current.Server.MapPath("~/App_Data");
MultipartFormDataStreamProvider provider = new MultipartFormDataStreamProvider(root);
//do stuff
var res = await Request.Content.ReadAsMultipartAsync(provider);
// DO SOME STUFF
}
catch (Exception exp)
{
}
return Request.CreateResponse(HttpStatusCode.OK, result);
}
I wanted to do the custom access validation for this web api, so implemented a filter to validate the request.
I have the filter like below
public class CustAuthAsyncAttribute : ActionFilterAttribute
{
public override async Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
{
InternalOnExecutingAsync(actionContext);
}
}
The internal method like this
protected void InternalOnExecutingAsync(HttpActionContext actionContext)
{
var authValue = actionContext.Request.Headers;
if (authValue.Contains("CustomAccessToken"))
{
string token = authValue.GetValues("CustomAccessToken").First();
var result = // doing some decription
if (result != null)
{
bool validationResult = // validation with database
if (!validationResult)
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized)
{ ReasonPhrase = "Invalid token" };
}
}
else
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized)
{ ReasonPhrase = "Invalid token" };
}
}
else
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized)
{ ReasonPhrase = "Unauthorized Request" };
}
These implementations are working fine in API Client Tools (Example: Postman) if the validation passes, allows the request to the method.
Postman Response screen shot
This is not working in mobile app, Saying the response message as Unauthorized Access. and not allowing the request to the method even the custom access validations are passed.
FYI : This method is working fine in mobile without filter
Help me to get this works in mobile app also.
Thanks in advance.
Your using the wrong type of filter to manage access. You should use an authorization filter. Besides you can't have an async method to authorize. You have to make the calling client wait for clearance. This may cause the side effects you're experiencing.
I'm not sure this has any to do with fact that it's a mobile application, however the authorization phase ir prior to the processing of the request. Verify that your are not using any other form of authorization in your project.
You should implement an authorization filter by inheriting AuthorizeAttribute and overriding IsAuthorized(HttpActionContext actionContext) method:
public class CustAuthAsync : AuthorizeAttribute
{
public CustAuthAsync()
{
///Some initialization if required. Otherwise, not necessary to declare the constructor..
}
protected override bool IsAuthorized(HttpActionContext actionContext)
{
var authValue = actionContext.Request.Headers;
if (authValue.Contains("CustomAccessToken"))
{
string token = authValue.GetValues("CustomAccessToken").First();
var result = // doing some decription
if (result != null)
{
return //database validation
}
else
{
return false;
//No need to create special unauthorized response. You should not hint the reason at this point. You can do this in the HandleUnauthorizedRequest method.
}
}
else
{
return false;//No need to create special unauthorized response.
}
}
}
You can use this attribute to decorate your controllers. You can even pass parameter in the constructor for more granular control on access management, like a required role to access de controller.

ServiceStack Validation RuleSet for Post is not working

i use ServiceStack build a web service,
this is my validator code:
public class AccountValidator : AbstractValidator<AccountModel>
{
public AccountValidator()
{
//only for post
RuleSet(ServiceStack.ApplyTo.Post, () =>
{
RuleFor(s => s.Password).Length(30);
});
}
}
Service code
public object Post(Account req) {
AccountModel model = req.ConvertTo<AccountModel>();
var result = this.Validator.Validate(model);
if (result.IsValid)
{
//is aways true
}
throw result.ToException();
}
the Account.Password is "abc" ,way the result.IsValid is true?
The result is always true because the ApplyTo.Post condition in your validator only applies to rules that are validated on the Request DTO. The ApplyTo conditions don't work for FluentValidation that is invoked manually. See the Validation Documentation.
But given that you are converting the Account into an AccountModel you could use the ValidationFeature plugin to check the validation of Account before converting it to an AccountModel.
Thus:
public class AccountValidator : AbstractValidator<Account>
{
public AccountValidator()
{
RuleSet(ApplyTo.Post, () => RuleFor(s => s.Password).Length(30));
}
}
// Account will be validated on POST
public object Post(Account req)
{
// Account is valid coming into the request
AccountModel model = req.ConvertTo<AccountModel>();
// Is it not safe to assume the model is valid, because Account is valid?
}

Post Scalar data type using HttpClient.PostAsJsonAsync

I am invoking ASP .Net Web API using HttpClient and invoke actions successfully. Also I am able to POST custom object into action as well.
Now problem I am facing is, not able to post scalar data type like Integer,String etc...
Below is my controller and application code that invokes action
// Test application that invoke
[Test]
public void RemoveCategory()
{
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage();
HttpResponseMessage response = client.PostAsJsonAsync<string>("http://localhost:49931/api/Supplier/RemoveCategory/", "9").Result;
Console.WriteLine(response.Content.ReadAsStringAsync().Result);
}
// Controller and Action in Web API
public class SupplierController : ApiController
{
NorthwindEntities context = new NorthwindEntities();
[HttpPost]
public HttpResponseMessage RemoveCategory(string CategoryID)
{
try
{
int CatId= Convert.ToInt32(CategoryID);
var category = context.Categories.Where(c => c.CategoryID == CatId).FirstOrDefault();
if (category != null)
{
context.Categories.DeleteObject(category);
context.SaveChanges();
return Request.CreateResponse(HttpStatusCode.OK, "Delete successfully CategoryID = " + CategoryID);
}
else
{
return Request.CreateResponse(HttpStatusCode.InternalServerError, "Invalid CategoryID");
}
}
catch (Exception _Exception)
{
return Request.CreateResponse(HttpStatusCode.InternalServerError, _Exception.Message);
}
}
When I Post custome object that represent "Category" table in Northwind database all things working properly but I am not able to post scalar data like Integer and String
When I am post string data type I am getting following exception
{"Message":"No HTTP resource was found that matches the request URI 'http://localhost:49931/api/Supplier/RemoveCategory/'.","MessageDetail":"No action was found on the controller 'Supplier' that matches the request."}
Can anyone guide me?
You will have to mark your CategoryID parameter as [FromBody]:
[HttpPost]
public HttpResponseMessage RemoveCategory([FromBody] string CategoryID)
{ ... }
By default, simple types such as string will be model bound from the URI.

Using file extension content negotiation with spring security plugin?

I'm trying to use the latest spring security plugin for grails, but I've hit a little bump.
I have a controller with this method:
#Secured(['ROLE_USER'])
def query = {
}
When I hit http://localhost:8080/myApp/myController/query, I get prompted for authorization as appropriate. However, I need to do content type negotiation via the filename extension. Using
grails.mime.file.extensions=true
I can use the same UrlMappings and get to my controller method via .../myApp/myController/query.js?params=blah. However, I am not prompted for authentication, and either the request goes through automatically or fails, depending on how I've set grails.plugins.springsecurity.rejectIfNoRule
How can I use file type negotiation with the spring security plugin?
Turn off grails.mime.file.extensions and add this filter:
class FileExtensionContentNegotiationFilters {
final static String DEFAULT_FORMAT = "js"
def filters = {
all(controller: '*', action: '*') {
before = {
addFormatToRequestByFileExtension(request)
}
after = {
}
afterView = {
}
}
}
protected addFormatToRequestByFileExtension(def request) {
String suffix = getSuffixFromPath(request.forwardURI)
String extension = FilenameUtils.getExtension(suffix)
if (extension.isEmpty()) {
request[GrailsApplicationAttributes.CONTENT_FORMAT] = DEFAULT_FORMAT
}
else {
request[GrailsApplicationAttributes.CONTENT_FORMAT] = extension
}
}
protected String getSuffixFromPath(String pathWithoutParams) {
int lastSlash = pathWithoutParams.lastIndexOf("/")
if (lastSlash < 0) {
return ""
}
return pathWithoutParams.substring(lastSlash + 1)
}
}
The solution above does not work for me as expected. A 404 response is generated when I request an URL with extension.
I come with another solution that does not need to turn off grails.mime.file.extensions and does not need an extra Filter. Instead, it's a modification of the plugin's class org.codehaus.groovy.grails.plugins.springsecurity.AnnotationFilterInvocationDefinition. What you need to do is to edit the method determineUrl as shown below (look at the comments to identify the changes):
#Override
protected String determineUrl(final FilterInvocation filterInvocation) {
HttpServletRequest request = filterInvocation.getHttpRequest();
HttpServletResponse response = filterInvocation.getHttpResponse();
GrailsWebRequest existingRequest = WebUtils.retrieveGrailsWebRequest();
String requestUrl = request.getRequestURI().substring(request.getContextPath().length());
/** The following 2 lines were added */
int indexOfPeriod = requestUrl.indexOf('.');
String requestUrlForMatching = (indexOfPeriod != -1) ? requestUrl.substring(0, indexOfPeriod) : requestUrl;
String url = null;
try {
GrailsWebRequest grailsRequest = new GrailsWebRequest(request, response,
ServletContextHolder.getServletContext());
WebUtils.storeGrailsWebRequest(grailsRequest);
Map<String, Object> savedParams = copyParams(grailsRequest);
/* Use requestUrlForMatching instead of requestUrl */
for (UrlMappingInfo mapping : _urlMappingsHolder.matchAll(requestUrlForMatching)) {
configureMapping(mapping, grailsRequest, savedParams);
url = findGrailsUrl(mapping);
if (url != null) {
break;
}
}
}
finally {
if (existingRequest == null) {
WebUtils.clearGrailsWebRequest();
}
else {
WebUtils.storeGrailsWebRequest(existingRequest);
}
}
if (!StringUtils.hasLength(url)) {
// probably css/js/image
url = requestUrl;
}
return lowercaseAndStripQuerystring(url);
}
The problem is that URLs with extensions do not match any URL in UrlMappings using UrlMappingsHolder.matchAll method. So, the solution is to ommit the extension prior to look for matches. With this changes everything works as expected.
I also create a pull request with the fix available at https://github.com/grails-plugins/grails-spring-security-core/pull/24
You can see the changes at https://github.com/arcesino/grails-spring-security-core/commit/19f87168ec4422b4fe06cc6914adeb1bae4b8752
Tested with version 1.2.7.3

How to populate Controller.Request in ASP.NET MVC

I want to use the Request, Response properties of System.Web.Mvc.Controller class to set and read cookies in the HTTP request and response. The reason to do so is - it obviates the need for writing utility classes that read from requests and populate data in some helper class. I can push all such code in custom base controller (from which all my controllers are derived from).
So I have got following code in my `BaseController'
if (Request != null)
{
HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
HttpContext.User = new GenericPrincipal(new GenericIdentity(authCookie.Value), null);
Thread.CurrentPrincipal = HttpContext.User;
}
}
but the Request is always null. How is this populated?
If you have this code in the constructor of your base controller then it is normal. You need to put it in the Initialize method. Also what you are doing shouldn't be done in a controller. Looking at your code you seem to be populating the HttpContext.User property: this should be done in a custom Authorize action filter.
For example:
public class MyAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(System.Web.HttpContextBase httpContext)
{
var result = base.AuthorizeCore(httpContext);
if (result)
{
var authCookie = httpContext.Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
httpContext.User = new GenericPrincipal(new GenericIdentity(authCookie.Value), null);
Thread.CurrentPrincipal = httpContext.User;
}
}
return result;
}
}
and then decorate your base controller with this attribute:
[MyAuthorize]
public abstract class BaseController: Controller
{}
Notice that this attribute requires the user to be authenticated in order to give access to the corresponding action so use it only on controllers/actions that require authentication.

Resources