Monday 9 October 2017

Token based authentication in ASP.NET Web API


Introduction

Series : Token based authentication using ASP.NET Web API in AngularJS

In one of my previous article, I have shown you how to implement custom Forms Authentication (cookie-based approach) in ASP.NET MVC application.
Today I am going to show you how to Secure ASP.NET Web API using Token Based Authentication.

ASP.NET Web API is a framework that makes it easy to build HTTP services that reach a broad range of clients, including browsers, mobile devices, and traditional desktop applications. Nowadays Web API adoption is increasing at a rapid pace. So it's very essential to implement security for all types of clients trying to access data from Web API services.

Nowadays the most preferred approach to secure server resources by authenticating users in WEB API is to use signed token, which contains enough data to identify a particular user.This is called token-based approach. This is because of following reason:

  • Loose Coupling - The client application is not tied to a particular authentication scheme. The token is generated, validated and perform the authentication by the server.
  • Mobile Friendly - In native platform like iOS, Android, Windows 8 etc. handling cookies are not an easy task. Token-based approach simplifies this a lot.
How token based authentication actually works?
In the Token based approach, the client application first sends a request to Authentication server endpoint with an appropriate credential. Now If the username and password are found correct then the Authentication server send a token to the client as a response. This token contains enough data to identify a particular user and an expiry time.The client application then uses the token to access the restricted resources in next requests till the token is valid. 

Follow the following steps in order to implement "Part 1 : Token based authentication using ASP.NET Web API 2".

Here In this article, I have used Visual Studio 2013

Step - 1: Create New Project.

Go to the file menu > create > projet > here select "asp.net web application" under web > enter application name > select your project location > and then click on add button ... >

    It will bring up a new dialog window for select template > here I will select empty template > and then checked MVC & Web API checkbox from Add folder and core references for > and then click on Ok button. 

Step-2: Add required references from NuGet packages into our application.

For Implement token based authentication in WEB API, we need to install followings references from NuGet packages

  1. Microsoft.Owin.Host.SystemWeb
  2. Microsoft.Owin.Security.OAuth
  3. Microsoft.Owin.Cors
for adding following resources from NuGet, Go to Solution Explorer >  Right Click on References > Click on Manage NuGet packages > Search for the Microsoft.Owin.Host.SystemWeb,  Microsoft.Owin.Security.OAuth & Microsoft.Owin.Cors and install. 

Step-3: Add a class for validating user credentials asking for tokens.

Now we will add a class in our application for validate the credentials for users and generate token.

For adding the class, go to solution explorer > Right click on your application name > add > New Item...> here we will select class. enter your class name> click on Add button.

In this class we will inherit "OAuthAuthorizationServerProvider" class for  overriding 2 methods "ValidateClientAuthentication" and "GrantResourceOwnerCredentials". "ValidateClientAuthentication" method is used for validating client app (for the sake of simplicity, we will  deep dive on "ValidateClientAuthentication" method later) and in the "GrantResourceOwnerCredentials"  method we will validate the credentials of users and if we found valid credential, we will generate the signed token, using which user can access authorized resources of server.

MyAuthorizationServerProvider.cs

  1. using Microsoft.Owin.Security.OAuth;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Security.Claims;
  6. using System.Threading.Tasks;
  7. using System.Web;
  8.  
  9. namespace webApiTokenAuthentication
  10. {
  11. public class MyAuthorizationServerProvider : OAuthAuthorizationServerProvider
  12. {
  13. public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
  14. {
  15. context.Validated(); //
  16. }
  17.  
  18. public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
  19. {
  20. var identity = new ClaimsIdentity(context.Options.AuthenticationType);
  21. if (context.UserName == "admin" && context.Password == "admin")
  22. {
  23. identity.AddClaim(new Claim(ClaimTypes.Role, "admin"));
  24. identity.AddClaim(new Claim("username", "admin"));
  25. identity.AddClaim(new Claim(ClaimTypes.Name, "Sourav Mondal"));
  26. context.Validated(identity);
  27. }
  28. else if (context.UserName == "user" && context.Password == "user")
  29. {
  30. identity.AddClaim(new Claim(ClaimTypes.Role, "user"));
  31. identity.AddClaim(new Claim("username", "user"));
  32. identity.AddClaim(new Claim(ClaimTypes.Name, "Suresh Sha"));
  33. context.Validated(identity);
  34. }
  35. else
  36. {
  37. context.SetError("invalid_grant", "Provided username and password is incorrect");
  38. return;
  39. }
  40. }
  41. }
  42. }


You can see in this (line-18 to line 40), I have used static data for validating user credential. later part of this series I will validate user from our database when we will implement client application (in AngularJS) for token based authentication.

Step-4: Add Owin Start Up class.

Now we will add OWIN Startup  class where we will Configure the OAuth Authorization Server.
Go to Solution Explorer > Right Click on Project Name form Solution Explorer > Add > New Item > Select OWIN Startup class > Enter class name > Add.

Startup.cs
  1. using System;
  2. using System.Threading.Tasks;
  3. using Microsoft.Owin;
  4. using Owin;
  5. using Microsoft.Owin.Security.OAuth;
  6. using System.Web.Http;
  7.  
  8. [assembly: OwinStartup(typeof(webApiTokenAuthentication.Startup))]
  9.  
  10. namespace webApiTokenAuthentication
  11. {
  12. public class Startup
  13. {
  14. public void Configuration(IAppBuilder app)
  15. {
  16. // For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=316888
  17. //enable cors origin requests
  18. app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
  19.  
  20. var myProvider = new MyAuthorizationServerProvider();
  21. OAuthAuthorizationServerOptions options = new OAuthAuthorizationServerOptions
  22. {
  23. AllowInsecureHttp = true,
  24. TokenEndpointPath = new PathString("/token"),
  25. AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
  26. Provider = myProvider
  27. };
  28. app.UseOAuthAuthorizationServer(options);
  29. app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
  30.  
  31.  
  32. HttpConfiguration config = new HttpConfiguration();
  33. WebApiConfig.Register(config);
  34. }
  35. }
  36. }

Step-5: Add an another Class for override authorize attribute.

When building an HTTP REST API, we should use appropriate HTTP response codes to indicate the status of a response. I always use 401 and 403 status code for getting authentication/authorization status. 401 (Unauthorized) - indicates that the request has not been applied because it lacks valid authentication credentials for the target resource. and 403 (Forbidden) - when the user is authenticated but isn’t authorized to perform the requested operation on the given resource.

Unfortunately, the ASP.NET MVC/Web API [Authorize] attribute doesn’t behave that way – it always emits 401. So, here in our Web API application, I am going to add a class for override this behavior. Here we will return 403 when the user is authenticated but not authorized to perform the requested operation.

AuthorizeAttribute.cs

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Web;
  5.  
  6. namespace webApiTokenAuthentication
  7. {
  8. public class AuthorizeAttribute : System.Web.Http.AuthorizeAttribute
  9. {
  10. protected override void HandleUnauthorizedRequest(System.Web.Http.Controllers.HttpActionContext actionContext)
  11. {
  12. if (!HttpContext.Current.User.Identity.IsAuthenticated)
  13. {
  14. base.HandleUnauthorizedRequest(actionContext);
  15. }
  16. else
  17. {
  18. actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Forbidden);
  19. }
  20. }
  21. }
  22. }

Step-6: Add WEB API Controller. 

Now we will add a WEB API Controller , Where we will add some action So we can check the token authentication is working fine or not.

Go to Solution Explorer > Right click on Controllers folder > Add > Controller > Select WEB API 2 Controller - Empty > Click on add button. > Enter controller name (in my case It's DataController.cs) > Add.

Step-7: Add an action for getting data from the server for all anonymous user.

I have added this action for all anonymous users. All type of request, whether it is authenticated or not can access this action.
  1. [AllowAnonymous]
  2. [HttpGet]
  3. [Route("api/data/forall")]
  4. public IHttpActionResult Get()
  5. {
  6. return Ok("Now server time is: " + DateTime.Now.ToString());
  7. }

Step-8: Add an another action for getting data from the server for all authenticated user.

I have added this action for all type of authenticated users, whether it is Admin user or normal user.
  1. [Authorize]
  2. [HttpGet]
  3. [Route("api/data/authenticate")]
  4. public IHttpActionResult GetForAuthenticate()
  5. {
  6. var identity = (ClaimsIdentity)User.Identity;
  7. return Ok("Hello " + identity.Name);
  8. }

Step-9: Add an another action for getting data from the server only for Admin user.

I have added this action only for Admin role type users. 
  1. [Authorize(Roles="admin")]
  2. [HttpGet]
  3. [Route("api/data/authorize")]
  4. public IHttpActionResult GetForAdmin()
  5. {
  6. var identity = (ClaimsIdentity)User.Identity;
  7. var roles = identity.Claims
  8. .Where(c => c.Type == ClaimTypes.Role)
  9. .Select(c => c.Value);
  10. return Ok("Hello " + identity.Name + " Role: " + string.Join(",", roles.ToList()));
  11. }

Step-10: Run Application.

OUR WEB API Configuration is ready, Now we will test our application with POSTMAN and then in the Next part of this series I have shown you how to create an AngularJS application (client application) for consuming our Web API services(backend service) secured with token based authentication.


Postman is an extension of Chrome, which is used as a client application to test the request and response between web service and client.

When you will now your application, you will get "The resource cannot be found" error message because we don't have anything in our root url now. But don't worry, we will do later.
What we need to do now, we have to open our POSTMAN for test our Web API. After that the followings link...

Test 1:
Select GET (see below picture section 1),  Enter this url http://localhost:/api/data/forall (see below picture section 2) and then click on send button.

We will get 200 OK status code (see section 3 in the below picture) and the result in the section 4 in that picture. That means our first action working fine when the request is anonymous.



But now if we try to access our 2nd action (GetForAuthenticate with url : http://localhost:/api/data/authenticate) then we will get 401 Unauthenticated because the request is not authenticated yet. It will same for the 3rd action also.
So, what we need to access the 2nd and 3rd action? We need access token from server first and then we can access the 2nd and 3rd action with that access token.

Test 2 : Getting access token.
Select POST (in section 1),  Enter this URL http://localhost:/token (in section 2) and then click on body (in section 3) and select select x-www-form-urlencoded and then enter 3 parameter, 1. username (value : user) 2. password (value: user) and 3. grant_type (value: password) and then click on  send button. After click on send button we will get 200 OK (see section 4) and access token (see section 5)



Now we can access http://localhost:/api/data/authenticate with that access token. 

Test 3: Access restricted resource with access token.
Select GET(in section 1),  Enter this URL http://localhost:/api/data/authenticate (in section 2) and then click on Headers(in section 3) and enter 1 parameter, Authorization (value : Bearer) and then click on  send button. After click on send button we will get 200 OK (see section 4) and the result (see section 5). 



In the same way, we can access our 3rd action but we have to get token logged in with username : admin and password: admin because the 3rd action accessible only for Admin role user.

No comments:

Post a Comment