Token Based Authentication Using ASP.Net Web API, OWIN and Identity With Entity Framework

Introduction

The Open Web Interface for .NET (OWIN) defines a standard interface between .NET web servers and web applications. Katana is open-source components for building and hosting OWIN-based web applications. It provides the implementation of the OWIN specification. The OAuth authorization framework enables a third-party application to obtain limited access to a HTTP service.

Currently the preferred approach to authenticate the users is to use a signed token and this token is sent to the server with each request. The following are the benefits for using this approach.

  • Scalability of Servers
    the token itself contains all the information of the user that is needed for authentication, so Web Farm extension is an easy task. There is no dependence on shared session stores.
  • Loosely Coupling
    Our front-end application is not coupled with a specific authentication mechanism. The token is generated from the server and our web API has a built-in way to understand this token and perform authentication.
  • Mobile Friendly
    This type of authentication does not require cookies, so this authentication type can be used with mobile applications.

Example

In the following demo application, the OAuth authorization server and the Web API endpoints will be hosted inside the same host.

Based Authentication

The following is the procedure to do Token Based Authentication using ASP.NET Web API, OWIN and Identity.

Step 1 - Create and configure a Web API project

Create an empty solution for the project template "ASP.NET Web Application" and add a core reference of the Web API and set the authentication to “No Authentication”.

Create and configure a Web API project

Update the current version of the Web API using the Nuget package with the following command.

            PM> Update-package Microsoft.AspNet.WebApi

WebApi

Step 2 - Install the required OWIN component using Nuget Packages

In this step, we need to install Nuget packages that are required to set up our OWIN server and configure the Web API to be hosted within the OWIN server. The "Microsoft.AspNet.Identity.Owin" package provides many useful extensions and we will use this while working with ASP.Net Identity on top of OWIN. It also downloads some other dependency packages. One of those dependency packages is "Microsoft.Owin.Security.OAuth". This is a core package required to support any standard OAuth 2.0 authentication workflow. The “Microsoft.Owin.Host.SystemWeb” namespace contains the types related to handling OWIN requests. It helps us to run OWIN-based applications on IIS using the ASP.NET request pipeline. Use the following commands to instal the OWIN server.

PM> Install-Package Microsoft.AspNet.Identity.Owin

PM> Install-Package Microsoft.Owin.Host.SystemWeb

ASP.NET Identity also supports the Entity Framework. Here we will use ASP.Net identity with Entity Framework, so we need to install this via Nuget packages.

PM> Install-Package Microsoft.AspNet.Identity.EntityFramework

This will also install the Entity Framework as a dependency.

Step 3 - Create a DbContext class

Create a DbContext class. The IdentityDbContext Class uses the default entity types for ASP.NET Identity Users, Roles, Claims and Logins. We can overload this to add our own entity types.

public class OwinAuthDbContext : IdentityDbContext  
{  
    public OwinAuthDbContext()  
        : base("OwinAuthDbContext")  
    {  
    }  
}

Step 4 - Do the migrations (optional step)

Entity Framework supports the database migration to create the database and insert some initial values. Migration commands can be executed from the Package Manager Console.

Migration commands

Step 4 - Define an OWIN Startup Class 

Every OWIN application has a startup class in which we specify components for the application pipeline. Here we are using the OwinStartup Attribute to connect to the startup class with the hosting runtime.

using Microsoft.Owin;  
using Owin;  
  
[assembly: OwinStartup(typeof(OwinAuthentication.Startup))]  
namespace OwinAuthentication  
{  
    public class Startup  
    {  
        public void Configuration(IAppBuilder app)  
        {  
  
        }  
    }  
}

Step 5 - Configure the OAuth Authorization Server

AppBuilderExtensions has the method "CreatePerOwinContext<T>" that registers a callback that will be invoked to create an instance of type T and it will be stored in the OwinContext. Later on we can retrieve it using the context.Get method. This method creates one instance of the given type per request. Here we are using ASP.Net Identity with Entity Framework, so we must create the instance of our DbContext class and to do this we use this extension method. The Microsoft.AspNet.Identity namespace has the class UserManager that exposes the use related to the API that automatically saves the changes to the UserStore. Here we will interact with our database using the UserManager class.

The following code is required to use the UserManager class inside our OWIN component efficiently.

private void ConfigureOAuth(IAppBuilder app)  
{  
    app.CreatePerOwinContext<OwinAuthDbContext>(() => new OwinAuthDbContext());  
    app.CreatePerOwinContext<UserManager<IdentityUser>>(CreateManager);  
}  
  
private static UserManager<IdentityUser> CreateManager (IdentityFactoryOptions<UserManager<IdentityUser>> options, IOwinContext context)  
{  
    var userStore = new UserStore<IdentityUser>(context.Get<OwinAuthDbContext>());  
    var owinManager = new UserManager<IdentityUser>(userStore);  
    return owinManager;  
}

The ConfigureOAuth method will be called inside the Configuration method of the OWIN startup class.

The UseOAuthAuthorizationServer extension method of OWIN is used to setup the authorization server. Following are the setup options: 

  • TokenEndpointPath
    requests the path on which the client application directly communicates to obtain the access token. It must begin with a leading slash, for example "/oauth/token". 
  • AuthorizeEndpointPath
    It is the request path where the client application will redirect the user-agent to obtain the user's consent to issue a token or code. It must begin with a leading slash (the same as TokenEndpointPath).
  • AllowInsecureHttp
    Set to true to allow authorize and token requests to arrive on HTTP URI addresses.
  • Provider
    the object provided by the application to process events raised by the Authorization Server. It may the instance of OAuthAuthorizationServerProvider and assign delegates necessary for the OAuth flow.
  • AuthorizationCodeProvider
    produces a single-use authorization code to return to the client application. It is required where the token is produced by the OnCreate/OnCreateAsync event.
  • RefreshTokenProvider
    produces a refresh token that may produce a new access token when required. If this option is not provided then the authorization server will not return refresh tokens from the Token endpoint.
  • ApplicationCanDisplayErrors
    Set to true when the web application is able to render error messages on the Authorize endpoint. This is required only in cases where the browser is not redirected back to the client application.
  • AccessTokenExpireTimeSpan
    The time period the access token remains valid after it was generated. The default value is 20 minutes.
  • AccessTokenFormat
    The data format used to protect the information contained by the access token. If it is not provided then the application will use the default data protection provider depending on the host server.
  • AccessTokenProvider
    It produces a bearer token.
  • AuthorizationCodeExpireTimeSpan
    The time period the authorization code remains valid after it was generated. The default value is 5 minutes
  • AuthorizationCodeFormat
    The data format used to protect and unprotect the information contained in the authorization code.
  • RefreshTokenFormat
    The data format used to protect and unprotect the information contained in the refresh token.
  • SystemClock
    Used to know what the current clock time is when calculating or validating token expiration. The default value is DateTimeOffset.UtcNow. 
private void ConfigureOAuth(IAppBuilder app)  
{  
    ...
    ... 
    app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions  
    {  
        TokenEndpointPath = new PathString("/oauth/token"),  
        Provider = new AuthorizationServerProvider(),  
        AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),  
        AllowInsecureHttp = true,  
                   
    });  
    app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());  
}

An Authorization Server uses a default implementation of IOAuthAuthorizationServerProvider to communicate with a web application while processing the request. OAuthAuthorizationServerProvider provides some default behavior like as used as a virtual base class and offers delegate properties that may be used to handle individual calls without creating the instance.

Here I just override the methods that I need here: ValidateClientAuthentication and GrantResourceOwnerCredentials.

ValidateClientAuthentication

It is called to validate that the requester (origin of the request) is a registered client_id and the correct credentials for that client are present on the request.

"OAuthValidateClientAuthenticationContext.TryGetBasicCredentials" may be able to retrieve the values of the client credential request header if the web application accepts basic authentication credentials. If the web application accepts a client id and secret as form-encoded POST parameters, "OAuthValidateClientAuthenticationContext.TryGetFormCredentials" can be used to retrieve this value. Finally, if "OAuthValidateClientAuthenticationContext.Validated” is not called then the request will not proceed further.

GrantResourceOwnerCredentials

 It is called when the request to the token endpoint arrives with a "grant_type" of "password". This occurs when the user provides a user id and password directly to the client application using the client application user interface. If the web application supports the resource owner credentials grant type, it must validate the username and password property of context. To issue the access token, the request must end with the “OAuthGrantResourceOwnerCredentialsContext.Validated" method. The default behavior is to reject this grant type.

public class AuthorizationServerProvider : OAuthAuthorizationServerProvider  
{  
    public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)  
    {  
    }  
    public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)  
    {  
    }  
}

The following is a probable implementation of the ValidateClientAuthentication method.  

public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)  
{  
    string clientId;  
    string clientSecret;  
  
    if (context.TryGetBasicCredentials(out clientId, out clientSecret))  
    {   
        // validate the client Id and secret against database or from configuration file.  
        context.Validated();  
    }  
    else  
    {  
        context.SetError("invalid_client", "Client credentials could not be retrieved from the Authorization header");  
        context.Rejected();  
    }  
}

The following is an implementation of the GrantResourceOwnerCredentials method.  

public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)  
{  
    UserManager<IdentityUser> userManager = context.OwinContext.GetUserManager<UserManager<IdentityUser>>();  
    IdentityUser user;  
    try  
    {  
        user = await userManager.FindAsync(context.UserName, context.Password);  
    }  
    catch  
    {  
        // Could not retrieve the user due to error.  
        context.SetError("server_error");  
        context.Rejected();  
        return;  
    }  
    if (user != null)  
    {  
        ClaimsIdentity identity = await userManager.CreateIdentityAsync(  
                                                user,  
                                                DefaultAuthenticationTypes.ExternalBearer);  
        context.Validated(identity);  
    }  
    else  
    {  
        context.SetError("invalid_grant", "Invalid User Id or password'");  
        context.Rejected();
    }
}

An HTTP POST request is made to the URL "/oauth/token" endpoint with grant_type parameter "password"; it will first arrive at the ValidateClientAuthentication method. In this place we can retrieve the client credentials and validate it. If the client credential is invalid, we need to return an unauthorized request using the context.Rejected method. If we grant the request in the ValidateClientAuthentication method, the request will arrive at the GrantResourceOwnerCredentials method. Inside this method, we need to validate that the user is using resource-owner credentials.

Step 6 - Test the Project

To test the preceding approach I created a console project in my solution. Create the following Token class within the console application.

using Newtonsoft.Json;  
  
namespace OWINTest  
{  
    public class Token  
    {  
        [JsonProperty("access_token")]  
        public string AccessToken { get; set; }  
        [JsonProperty("token_type")]  
        public string TokenType { get; set; }  
        [JsonProperty("expires_in")]  
        public int ExpiresIn { get; set; }  
        [JsonProperty("refresh_token")]  
        public string RefreshToken { get; set; }  
        [JsonProperty("error")]  
       public string Error { get; set; }  
    }  
}

Program.cs code  

using System;  
using System.Collections.Generic;  
using System.Net.Http;  
using System.Net.Http.Formatting;  
  
namespace OWINTest  
{  
    class Program  
    {  
        static void Main(string[] args)  
        {  
            string baseAddress = "http://localhost:4312";  
            using (var client = new HttpClient())  
            {  
                var form = new Dictionary<string, string>    
               {    
                   {"grant_type", "password"},    
                   {"username", "jignesh"},    
                   {"password", "user123456"},    
               };  
                var tokenResponse = client.PostAsync(baseAddress + "/oauth/token", new FormUrlEncodedContent(form)).Result;  
                //var token = tokenResponse.Content.ReadAsStringAsync().Result;  
                var token = tokenResponse.Content.ReadAsAsync<Token>(new[] { new JsonMediaTypeFormatter() }).Result;  
                if (string.IsNullOrEmpty(token.Error))  
                {  
                    Console.WriteLine("Token issued is: {0}", token.AccessToken);  
                }  
                else  
                {  
                    Console.WriteLine("Error : {0}", token.Error);  
                }  
                Console.Read();  
            }  
        }  
    }  
} 

Using the following script, insert some dummy data into the AspNetUser table. Here I have encrypted my password using Abstraction for the password hashing methods of the Microsoft.AspNet.Identity namespace available with the UserManger class.

INSERT [dbo].[AspNetUsers] ([Id], [Email], [EmailConfirmed], [PasswordHash], [SecurityStamp],   
[PhoneNumber], [PhoneNumberConfirmed], [TwoFactorEnabled], [LockoutEndDateUtc], [LockoutEnabled],   
[AccessFailedCount], [UserName])   
VALUES (N'9f15bdd0fcd5423190c2e877ba0228ee', N'[email protected]', 1,   
N'ALkHGax/i5KBYWJ7q4jhJmMKmm2quBtnnqS8KcmLWd2kQpN6FaGVulDmmX12s7YAyQ==',   
N'a7bc5c5c-6169-4911-b935-6fc4df01d313', NULL, 0, 0, NULL, 0, 0, N'Jignesh')  

Output

JSON

We receive the response in the form of JSON and we convert it into our Token class using the Content.ReadAsAsync method. The Result Token class contains either the access token or an error. If we pass the wrong credentials, the system will generate the error:

error

In the next request we use this token for the authentication and the token will be sent in the request header. The Token is valid up to its expiry time.

To test it, I added a controller to my Web API project and created a test method as in the following.

using System.Web.Http;  
  
namespace OwinAuthentication.Controllers  
{  
    [Authorize]  
    public class POCController : ApiController  
    {  
        [HttpGet]  
        [Route("api/TestMethod")]  
        public string TestMethod()  
        {  
            return "Hello, C# Corner Member. ";  
        }  
    }  
}

Here we must mark our controller or action method with the Authorize attribute to check whether the request has a valid token.

The following is needed to append in a client application (console application in our case). First of all we need to get the token using the code described in the preceding section and then use this token to process the request.

using (HttpClient httpClient1 = new HttpClient())  
{  
    httpClient1.BaseAddress = new Uri(baseAddress);  
    httpClient1.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", token.AccessToken);  
    HttpResponseMessage response = httpClient1.GetAsync("api/TestMethod").Result;  
    if (response.IsSuccessStatusCode)  
    {  
        System.Console.WriteLine("Success");  
    }  
    string message = response.Content.ReadAsStringAsync().Result;  
    System.Console.WriteLine("URL responese : " + message);  
}

Output

Output

Summary

Using this article we can set up our authorization server and we have a working OAuth 2.0 token endpoint that only supports "Resource Owner Password Credentials Grant" as of now. 


Similar Articles