In our previous discussions, we discussed about the basics about the security in asp.net web api. If you have not read that then i would recommend you to read that first here, as it will act as a base of where and how to apply the security in asp.net web api, as we discussed the basics of security. Also we discussed the concept of how we can Implement Basic Authentication in ASP.Net Web API at Host level (IIS).
Continuing on same lines, we will implement basic authentication using the concept of Authentication filters. An authentication filter is nothing but creating an attribute and applying it on the class or method.
Let's browse the api method using the path: and see the results. We can easily view the results. No username/password required.
Anybody, who knows the url of your api, can make call to it and use the method and we would like to restrict this, so that only people who are authorized to access the system, should get access to the api method. So, in order to apply security on our method, we need to do the following:
Continuing on same lines, we will implement basic authentication using the concept of Authentication filters. An authentication filter is nothing but creating an attribute and applying it on the class or method.
Let's browse the api method using the path: and see the results. We can easily view the results. No username/password required.
Anybody, who knows the url of your api, can make call to it and use the method and we would like to restrict this, so that only people who are authorized to access the system, should get access to the api method. So, in order to apply security on our method, we need to do the following:
- Create an authentication filter, which is nothing but a class derived from IAuthenticationFilter interface and Attribute class. It will implement the following IAuthenticate methods. These are:
- AuthenticateAsync
- ChallengeAsync
- Apply this filter as an attribute on the method or the controller, on which we would like to add the security.
- Apply the Authorize attribute along with this attribute.
How the security process will work ?
When the IAuthenticationFilter is applied, AuthenticateAsync is the method which will receive any request for the web-api methods and check for the credentials(if they are present), in the request header. There are 3 possible scenarios:
Case 1 - No credentials provided: Client request has not sent any credentials. In such a case, authentication filter will do nothing and pass it on to Authorize attribute to handle it. Since there were no credentials, and identity was NOT authenticated, the request is rejected with Code 401 unauthorized result.
Case 2 - Invalid credentials provided: In case the client has provided invalid credentials, the ErrorResult property of the current HttpAuthenticationContext instance is set with UnauthorizedResult. An authentication challenge is added to the Result property of the current HttpAuthenticationContext instance. This is done by the ChallengeAsync method of the IAuthenticationFilter interface.
Case 3 - Valid credentials provided: If the credentials provided in the request header are valid, then a valid ClaimPrincipal will be generated and will be assigned to the Principle property of the current HttpAuthenticationContext instance. Further the Authorize attribute will check if the Identity is established for the request, then allow the access to the method or controller.
Case 1 - No credentials provided: Client request has not sent any credentials. In such a case, authentication filter will do nothing and pass it on to Authorize attribute to handle it. Since there were no credentials, and identity was NOT authenticated, the request is rejected with Code 401 unauthorized result.
Case 2 - Invalid credentials provided: In case the client has provided invalid credentials, the ErrorResult property of the current HttpAuthenticationContext instance is set with UnauthorizedResult. An authentication challenge is added to the Result property of the current HttpAuthenticationContext instance. This is done by the ChallengeAsync method of the IAuthenticationFilter interface.
Case 3 - Valid credentials provided: If the credentials provided in the request header are valid, then a valid ClaimPrincipal will be generated and will be assigned to the Principle property of the current HttpAuthenticationContext instance. Further the Authorize attribute will check if the Identity is established for the request, then allow the access to the method or controller.
So one of the major role of Authorize attribute is checking whether an identity for the user has been established or not by the authentication filters, which were executed previously in the pipeline. If it is not established, then return 401 Unauthorized error, else request will be allowed to access the method.
So let's start by creating the authentication filter class. We will name it as BasicAuthenticationFilter. We will derive it from Attribute class and IAuthenticationFilter. So our code will look like the following:
Attribute class will make it possible to apply it as an attribute on the method or controller. AuthenticateAsync will be used for authenticating the request credentials, if present, and ChallengeAsync will be used to prepare the final HttpResponse, based on the result of ExecuteAsync method of the current HttpAuthenticationChallengeContexts' context.
Now simply apply this filter and the authorize attribute on our method, like any other attribute:
Now let's test the 3 cases that we discussed above.
Case 1: Run the application and you will be prompted for a username/password.
Click Cancel and see that you get the message Authorization has been denied for this request.
Now simply apply this filter and the authorize attribute on our method, like any other attribute:
Now let's test the 3 cases that we discussed above.
Case 1: Run the application and you will be prompted for a username/password.
Click Cancel and see that you get the message Authorization has been denied for this request.
This was because, when the authentication filter received the request, no credentials were provided. So it passed on the request to the Authorize attribute. Due to absence of an authenticated principle, it rejected the request and this message was received.
Case 2: Run the api again and this time enter invalid credentials. This time, the authenticate method will check for the credentials. Since the credentials were not valid, it will set the ErrorResult property of the current HttpAuthenticationContext instance.
Case 3: Since the credentials were invalid, it will again ask for the credentials. This time, we will provide the valid credentials and see the code get's executed:
This time the credentials were valid. So an Identity is established for the current request (as above) and method result is returned.
Now a point that I have mentioned above, about the ExecuteAsync is very important. This means that when ever the ChallengeAsync method get's executed, it will get the HttpResponse generated for the request, from the ExecuteAsync method. So if the ExecuteAsync method says that the request is UnAuthorized request, we will check the response and set the basic authentication challenge in the response. Read a very important point here, from the official asp.net web-api. It says:
It's important to understand that ChallengeAsync is called before the HTTP response is created, and possibly even before the controller action runs. When ChallengeAsync is called,
context.Result
contains an IHttpActionResult, which is used later to create the HTTP response. So when ChallengeAsync is called, you don't know anything about the HTTP response yet. The ChallengeAsync method should replace the original value of context.Result
with a new IHttpActionResult. This IHttpActionResult must wrap the original context.Result
.The best way to test this point is to remove the Authorize attribute and add a debugger to the ChallengeAsync and the api method we need to access. When we run the request, we can see that the ExecuteAsync will be able to execute the api method (in our case it is GetList) and HttpStatusCode as OK will be returned. This means the HttpResponse was successfully generated for the request, by the ExecuteAsync method. Had we applied the Authorize attribute, it would have returned UnAuthorized status and based on the results it returned, we can then add our authentication challenge on the response from the ExecuteAsync.
So this was how we can implement the basic authentication filter using web-api Authentication filters. Hope you enjoyed reading it. Happy coding...!!!
No comments:
Post a Comment