Recently we've been working on a web API project in ASP.NET Core and the majority of our endpoints require a user to be logged in when they are invoked. Luckily it's easy to apply the built in Authorize attribute to any controller class or method to restrict access to only authenticated users.

Applying Authorize at a class level is the same as applying it directly to every method in the class.

If the Authorize attribute was ever removed it would allow API endpoints to be invoked by unauthenticated users, representing a serious security vulnerability in the system. To partly mitigate the risk it would be great to write a unit test to confirm the presence of the Authorize attribute on a class or method.

Testing at a class level

The following example controller is secured at the class level:

[Route("api/[controller]")]
[ApiController]
[Authorize]
public class ProfileController : ControllerBase
{
    // GET api/profile
    [HttpGet]
    public ActionResult<string> GetProfile()
    {
        return "Closed endpoint only authenticated users can call";
    }
}

We can add an xUnit.net test to verify the existence of the attribute using a little reflection magic:

[Fact]
public void ProfileControllerHasTheAuthorizeAttribute()
{
    // Act
    var attribute = typeof(ProfileController)
    	.GetCustomAttributes(typeof(AuthorizeAttribute), false)
        .SingleOrDefault();

    // Assert
    Assert.NotNull(attribute);
}

The test chains a number of method calls which query the attributes attached to the ProfileController class. Stepping through each of these calls one at a time:

1. typeof(ProfileController) returns an instance of System.Type which acts as a gateway into the reflection functionality built into .NET. Crucially this allows us to begin to drill into the methods, properties, constructors etc. that are contained in the class.

2. GetCustomAttributes(typeof(AuthorizeAttribute), false) returns an object array and in this case we're passing the type of the attribute we would like to retrieve, AuthorizeAttribute, as the first parameter. The second parameter, the boolean, indicates whether we would like to search the inheritance chain as well as looking on the class directly which could be handy if we has a SecureController base class we wanted to inherit from.

3. SingleOrDefault() is the final piece of the puzzle and assumes either a single attribute or nothing has been returned inside the object array generated from the GetCustomAttributes call. If the array does contain a single attribute it'll be assigned into the attribute variable however if the array is empty the attribute variable will be null. If for any reason we have more than one [Authorize] attribute on the class an exception would be thrown.

Testing at a method level

The following example controller has one secured method and one unsecured:

[Route("api/[controller]")]
[ApiController]
public class NewsController : ControllerBase
{
    // GET api/news
    [HttpGet]
    public ActionResult<string> GetNewsArticle()
    {
        return "Open endpoint everyone can call";
    }

    // POST api/news
    [HttpPost]
    [Authorize]
    public ActionResult<string> CreateNewsArticle()
    {
        return "Closed endpoint only authenticated users can call";
    }
}

We can tweak our previous test slightly to operate on a single method rather than the class as a whole:

[Fact]
public void CreateNewsAtricleMethodHasTheAuthorizeAttribute()
{
    // Act
    var attribute = typeof(NewsController)
        .GetMethod("CreateNewsArticle")
    	.GetCustomAttributes(typeof(AuthorizeAttribute), false)
        .SingleOrDefault();

    // Assert
    Assert.NotNull(attribute);
}

Adding the GetMethod("CreateNewsAtricle") call returns an instance of System.Reflection.MethodInfo giving us access to metadata on the method such as it's visibility (public, private etc.) and it's attributes which are then queried using the same GetCustomAttributes call we used above.

Wrapping up

Although we've only talked about the Authorize attribute in this post the same approach can be used to test the presence, or not, of any type of attribute on a class or method.

A bare bones ASP.NET Core API project containing the above code is available on our GitHub.