14

I need a way to check if "allow anonymous" is on/off in the controller action. Whether it comes from controller attribute, action attribute

[AllowAnonymous]

or it is set as filter in the MvcOptions

opts.Filters.Add(new AllowAnonymousFilter());

Is it possible?

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
moozywu
  • 209
  • 1
  • 2
  • 10
  • 1
    This feels like you're trying to do something else, maybe it is an [XY Problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). What problem are you trying to solve by doing this? – phuzi Mar 04 '20 at 10:09
  • 1
    I have one API endpoint, that has to secure images. So you have one time use token, that you need to pass as query, because of browsers. For this one endpoint implementation of custom authenticator would be too much I think. On the other hand for end to end tests and dev purposes I need to tell if it should authenticate. – moozywu Mar 04 '20 at 10:16

5 Answers5

32

It seems you need to check whether a controller and action does contain AllowAnonymousAttribute from your custom authentication filter during a request. So you can do this as follows:

public class CustomAuthorizationFilter : IAsyncAuthorizationFilter
{
    public async Task OnAuthorizationAsync(AuthorizationFilterContext filterContext)
    {
        if (filterContext == null)
        {
            throw new ArgumentNullException(nameof(filterContext));
        }

        bool hasAllowAnonymous = filterContext.ActionDescriptor.EndpointMetadata
                                 .Any(em => em.GetType() == typeof(AllowAnonymousAttribute)); //< -- Here it is

        if (hasAllowAnonymous) return;

        // Do your authorization check here
    }
}
TanvirArjel
  • 30,049
  • 14
  • 78
  • 114
  • 1
    This wouldn't work if AllowAnonymousFilter is set in MvcOptions, at least for dotnet core 3.1. Scan through filterContext.ActionDescriptor.FilterDescriptors will do the work. – Aaron Tee Nov 17 '20 at 06:22
  • I have the issue that my controller allows anonymous, but my method uses the authorize attribute. Any allows it even though it should not. How can I see what attributes relate to the controller and what relates to the method? – spmoolman Mar 16 '22 at 12:15
  • Is it possible to check this within custom `AuthenticationHandler`? We have a custom authentication handler and running into an issue with Anonymous routes – kkdeveloper7 Mar 30 '23 at 15:42
2

For checking, whether a controller and action has AllowAnonymous attribute, then in your custom authorization filter you can do this as follows:

For method level allow anonymous attribute:

bool hasAllowAnonymous = = filterContext.ActionDescriptor.EndpointMetadata
                             .Any(em => em.GetType() == typeof(AllowAnonymousAttribute));

For controller level allow anonymous attribute:

bool hasAllowAnonymous = = filterContext.ActionDescriptor.ControllerDescriptor.EndpointMetadata
                             .Any(em => em.GetType() == typeof(AllowAnonymousAttribute));

And then

if (hasAllowAnonymous) return;
mohammadAli
  • 375
  • 2
  • 13
0

Given the extra info in your comment. This appears to work the same way you should do a password reset.

For a password reset you include a one-time password reset token in the request (along with the password/verified password etc.) I would explicitly decorate the action with [AllowAnonymous] and validate the token in the before updating any details and cancelling the token.

In your case, I would do the same thing - explicitly decorate the action with [AllowAnonymous] and validate the one-time token. Your action shouldn't care whether the AllowAnonymous filter has been applied to the Action via the attribute or options.

UPDATE Been thinking a bit more and there's an easy way to disable this based on the build configuration. Wrap the attribute in an #if preprocessor directive and create a build configuration that defines a conditional compliation symbol. See this answer for details

#if DISABLE_ALLOW_ANONYOMOUS
    [AllowAnonymous]
#endif
    public IActionResult GetPicture(string token){
        ...

You can then build a test specific version where AllowAnonymous is disabled.

You could also do the same with MvcOptions:

#if DISABLE_ALLOW_ANONYOMOUS
    opts.Filters.Add(new AllowAnonymousFilter());
#endif
phuzi
  • 12,078
  • 3
  • 26
  • 50
  • This is my current state actually. But I want to be able to turn off, by AllowAnonymousFilter. for example my controller action would start like this. if (!IsAnonymousAllowed) await _authenticationService.AuthorizeFileAsync(accessToken, requestedObject, requestedFilename); – moozywu Mar 04 '20 at 11:47
  • Why do you want to be able to turn it off? – phuzi Mar 04 '20 at 12:04
  • Sounds a bit fishy, if you are turning it off, then you're not testing the code/paths through the code that exist in the production environment. Seems to me that doing so would invalidate the test. – phuzi Mar 04 '20 at 12:38
  • Also, I would have thought that leaving it enabled would actually be the easier - no need to check user credentials and no need for all the data that doing so requires. – phuzi Mar 04 '20 at 12:42
  • Might also be a good idea to edit the original question with all this extra info. – phuzi Mar 04 '20 at 12:44
  • The project is really large with a lot of micro-services and pretty large team. There are lots of tests, and of coarse authentication is covered. But for some internal reasons some of tests can't be. That's great idea, I will edit it in few minutes. – moozywu Mar 04 '20 at 13:10
  • Thought of another way to do what you're asking, check the update. – phuzi Mar 04 '20 at 13:48
0
public class MyController : Controller
{
    protected override void OnAuthorization(AuthorizationContext filterContext)
    {
        var anonActionAttributes = filterContext.ActionDescriptor
            .GetCustomAttributes(typeof(System.Web.Mvc.AllowAnonymousAttribute), true);

        var anonControllerAttributes = filterContext.ActionDescriptor.ControllerDescriptor
            .GetCustomAttributes(typeof(System.Web.Mvc.AllowAnonymousAttribute), true);

        if (anonActionAttributes.Length > 0 || anonControllerAttributes.Length > 0)            
            IsAllowAnonymous = true;

        base.OnAuthorization(filterContext);
    }

    bool IsAllowAnonymous { get; set; } = false;    
}

If you found this answer helpful, kindly upvote it.

David Sopko
  • 5,263
  • 2
  • 38
  • 42
0

If you need to check if the [AllowAnonymous] attribute is used on a WebAPI2 controller action, when using an ActionFilterAttribute this should work

public class SomeCheckFilter : System.Web.Http.Filters.ActionFilterAttribute
 {
   public override void OnActionExecuting(HttpActionContext actionContext)
    {
      bool hasAllowAnonymous = actionContext.ActionDescriptor.GetCustomAttributes(typeof(AllowAnonymousAttribute)).Any();
      if (hasAllowAnonymous == false)                                             
       {
        // if NOT Anonymous, do the Filtercheck  
        ...    
       }
    }
 } 
BigJoeNH
  • 381
  • 3
  • 6