Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#4.0

Token Based Authentication for Web API where Legacy User Database is used

4.95/5 (19 votes)
18 May 2017CPOL4 min read 84.2K  
Token Based Authentication for Web API

Introduction

This article gives a detailed explanation on how to use Token Based Authentication using OAuth and OWIN where application is using custom database having user credentials stored in legacy format.

Background

Since many days, I was going through articles about ASP.NET Web API “token based authentication”. Almost all were using ASP.NET Identity for user management features. (Which creates ASPNET* tables to manage users, roles, groups, etc.).

But I was unable to find an article which will show how to use OWIN & OAuth “token based authentication” for existing database having custom User management tables. This is necessary when it comes to apply OAuth authentication to legacy systems having their own user management feature already in place.

So I am writing this article after analyzing a standard template given by Visual Studio for Web API Individual Accounts authentication and tweaking it to use custom DB User tables. I have used Visual Studio 2015 for this.

Using the Code

As discussed above, our problem is, how an existing database User table having UserName, Password stored, can leverage Token based Authentication using OAuth and OWIN. So our typical DB query will look like below. (I have not considered Password encryption for now. I leave it to your application logic.)

Image 1

Let’s start by creating a standard ASP.NET Web API project as shown below. Please select Web API option from New Project template. Note, we are using “No Authentication” option as we are going to configure it manually.

Image 2

Since we want to use OAuth for authentication and Entity Framework for DB access, install the below Nuget packages: (They will install their dependants automatically.)

EntityFramework
Microsoft.AspNet.WebApi.Owin
Microsoft.Owin.Security
Microsoft.AspNet.Identity.Owin
Microsoft.Owin.Host.SystemWeb

Next, we need to add Entity Framework model connecting to our DB User Table. I won’t go in detail of that as it is a standard procedure of adding ADO.NET Entity Data Model. Resultant EDMX file should look like below:

Image 3

Next, we need to add OWIN Startup class. It will be a partial class. Add “Startup.Auth.cs” under App_Start folder and paste the below code in it:

C#
public partial class Startup
    {
        public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }

        public static string PublicClientId { get; private set; }

        // For more information on configuring authentication, 
        // please visit http://go.microsoft.com/fwlink/?LinkId=301864
        public void ConfigureAuth(IAppBuilder app)
        {
            // Configure the application for OAuth based flow
            PublicClientId = "self";
            OAuthOptions = new OAuthAuthorizationServerOptions
            {
                TokenEndpointPath = new PathString("/Token"),
                Provider = new ApplicationOAuthProvider(PublicClientId),
                AuthorizeEndpointPath = 
                new PathString("/api/Account/ExternalLogin"),
                AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(10),
                // In production mode set AllowInsecureHttp = false
                AllowInsecureHttp = true
            };

            // Enable the application to use bearer tokens to authenticate users
            app.UseOAuthBearerTokens(OAuthOptions);
        }
    }

Again, add “Startup.cs” class at project level and paste the below code in it. Replace namespace string with your project namespace.

C#
[assembly: OwinStartup(typeof(OAuth_Custom_DB.Startup))]
namespace OAuth_Custom_DB
{
    public partial class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            ConfigureAuth(app);
        }
    }
}

Note: Since Startup is a partial class, its namespace should be the same in Startup.cs and Startup.Auth.cs.

Next, we need to tell Web API application to use Bearer Authentication and not Default Host Authentication. So add the below lines to WebApiConfig.cs.

C#
// Web API configuration and services
// Configure Web API to use only bearer token authentication.
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));

Next, we need to configure OAuth server and options. So add a folder named “Providers” and a class file named “ApplicationOAuthProvider.cs” under it.

Image 4

C#
public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
    {
        private readonly string _publicClientId;

        public ApplicationOAuthProvider(string publicClientId)
        {
            if (publicClientId == null)
            {
                throw new ArgumentNullException("publicClientId");
            }

            _publicClientId = publicClientId;
        }

        public override async Task GrantResourceOwnerCredentials
        (OAuthGrantResourceOwnerCredentialsContext context)
        {
            /*** Replace below user authentication code as per your Entity Framework Model ***
            using (var obj = new UserDBEntities())
            {
                
                tblUserMaster entry = obj.tblUserMasters.Where
                <tblUserMaster>(record => 
                record.User_ID == context.UserName && 
                record.User_Password == context.Password).FirstOrDefault();

                if (entry == null)
                {
                    context.SetError("invalid_grant", 
                    "The user name or password is incorrect.");
                    return;
                }                
            }
            */

            ClaimsIdentity oAuthIdentity = 
            new ClaimsIdentity(context.Options.AuthenticationType);
            ClaimsIdentity cookiesIdentity = 
            new ClaimsIdentity(context.Options.AuthenticationType);

            AuthenticationProperties properties = CreateProperties(context.UserName);
            AuthenticationTicket ticket = 
            new AuthenticationTicket(oAuthIdentity, properties);
            context.Validated(ticket);
            context.Request.Context.Authentication.SignIn(cookiesIdentity);
        }

        public override Task TokenEndpoint(OAuthTokenEndpointContext context)
        {
            foreach (KeyValuePair<string, 
            string> property in context.Properties.Dictionary)
            {
                context.AdditionalResponseParameters.Add(property.Key, property.Value);
            }

            return Task.FromResult<object>(null);
        }

        public override Task ValidateClientAuthentication
        (OAuthValidateClientAuthenticationContext context)
        {
            // Resource owner password credentials does not provide a client ID.
            if (context.ClientId == null)
            {
                context.Validated();
            }

            return Task.FromResult<object>(null);
        }

        public override Task ValidateClientRedirectUri
        (OAuthValidateClientRedirectUriContext context)
        {
            if (context.ClientId == _publicClientId)
            {
                Uri expectedRootUri = new Uri(context.Request.Uri, "/");

                if (expectedRootUri.AbsoluteUri == context.RedirectUri)
                {
                    context.Validated();
                }
            }

            return Task.FromResult<object>(null);
        }

        public static AuthenticationProperties CreateProperties(string userName)
        {
            IDictionary<string, string> 
            data = new Dictionary<string, string>
            {
                { "userName", userName }
            };
            return new AuthenticationProperties(data);
        }
    }

Actually, it is the same file which gets generated when we select “Individual Accounts” option in Authentication mode while selecting Web API Project template. The only difference is that we are changing the implementation of “GrantResourceOwnerCredentials” method. Code inside the method should look like below:

C#
/*** Replace below user authentication code as per your Entity Framework Model ***
using (var obj = new UserDBEntities())
            {
                
                tblUserMaster entry = obj.tblUserMasters.Where<tblUserMaster>
                (record => record.User_ID == context.UserName &&
                record.User_Password == context.Password).FirstOrDefault();

                if (entry == null)
                {
                    context.SetError("invalid_grant", 
                    "The user name or password is incorrect.");
                    return;
                }                
            }
            */

            ClaimsIdentity oAuthIdentity = 
            new ClaimsIdentity(context.Options.AuthenticationType);
            ClaimsIdentity cookiesIdentity = 
            new ClaimsIdentity(context.Options.AuthenticationType);

            AuthenticationProperties properties = CreateProperties(context.UserName);
            AuthenticationTicket ticket = 
            new AuthenticationTicket(oAuthIdentity, properties);
            context.Validated(ticket);
            context.Request.Context.Authentication.SignIn(cookiesIdentity);

Last thing, apply normal [Authorize] attribute to ValuesController controller for testing purposes.

That’s it… !!!

Our application is ready to be tested for Token based authentication having a custom user ID/Password table.

Run the application so that API service starts running and is ready to be consumed. Your web page should look like below except the port number after locahost:

Image 5

Now let’s test standard Web API URL directly if it works without authorization.

Open Chrome Postman and type GET URL for Values Controller. Something like http://localhost:56889/api/Values. It gives Authorization denied error as seen below, obviously because there is no authorization done.

Image 6

So now, we will obtain a "Bearer Token" after authenticating user credentials. So our Token URL will as follows: http://localhost:56889/Token. Note, “/token” is the path specified as “TokenEndPoint” in “ConfigureAuth” method of Startup class. So if it is configured as “/auth/token”, the complete URL would be http://localhost:56889/Auth/Token likewise.

If you look at the Postman entries, for Token URL, we are passing three parameters with content-type as “x-www-form-urlencoded”. One of the parameters is “grant_type” and its value is “password”. This tells OAuth that user wants token to be issued.

Image 7

After successful execution of the POST url, we get token issued as below:

Image 8

What happened here is, this URL has called GrantResourceOwnerCredentials method. In this method, we have written our custom code of User authentication using Entity Framework. If it succeeds, it executes TokenEndpoint method further to issue a bearer token.

Take a note of this token value and re-visit the GET URL of ValuesController. This time, we will be adding a header named “Authorization” and paste token value we have got after authentication as its value with pre-fixing it with “bearer “.

Image 9

Now this token will be valid till the time we have specified again in OAuthAuthorizationServerOptions in ConfigureAuth method in Startup class.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)