Today I am going to talk about tweaking authentication in a .NET CORE 3.1 application which implements a custom authentication scheme. The customization is achieved by making use of “Attributes.” When it comes to authentication and protecting a service endpoint in a Web API, the most common practice is for the consumer to present an authentication token. This way we can make sure secure data is only presented to valid users.
Authenticating a service can be achieved in many ways, but in this example, I will be referring to custom authentication scheme. So, I created a custom ‘AuthenticationHandler’ which overrides ‘HandleAuthenticateAsync’ method. Whenever a user requests a protected resource and presents an authentication token, the framework invokes this method to validate the token and set up claims in the Principal. Here I wrote custom validation methods to read the token and populate the user context with custom information required for our application.
This was all working well until I was faced with a unique challenge. There are two different type of users in our system, one of which has elevated rights in the application. Both these types of users have similar application-level permissions, the only real difference between these two types of users is in the kind of token they present to the API. Most of the APIs are available to both type of users, but there are few restricted endpoints that are available only to the users with elevated rights.
Usually, this type of scenario can be addressed using ‘AuthorizationFilterAttribute,’ but because of the unique way permissions are set up in our system, this was not an option for me. The requirement boils down to authenticating all users who presents a valid token but deny access to few endpoints if they do not have an additional attribute in the authentication token. The opposite of this scenario can be easily achieved using an ‘[AllowAnonymous]’ filter attribute at the controller level or at each API endpoint level where certain endpoints are available to everyone despite the status of their Authentication Token. This will bypass any authentication to make the controller/method public.
I could not write code to always authenticate one type of user vs. the other. It was totally based on which endpoint the user was trying to access. One option I have is to do the validation at each method level to check if the user satisfies the criteria before executing the method. But this is not the most efficient way to achieve the desired output. In order to address this requirement, I decided to create my own attribute like ‘AllowAnonymous’ so that developers can use it on the special endpoints/controllers to indicate that it requires additional validation of Auth token.
First I created an attribute class:
Then I updated my token validation login inside the ‘HandleAuthenticateAsync’ override method to look for this attribute in the ‘HttpContext’ endpoints. If the requested method has this attribute, the validation function will perform additional checks to make sure the presented token satisfies the requirement. To check if the requested method has the attribute, we can use the below code:
Finally, for this to work without any issues, while configuring HTTP request pipeline, add authentication after routing.