Click here to Skip to main content
15,884,388 members
Articles / Programming Languages / C# 4.0

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

Rate me:
Please Sign up or sign in to vote.
4.95/5 (19 votes)
18 May 2017CPOL4 min read 80.5K   28   30
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)


Written By
Web Developer
India India
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Questionhow can i call this API project from my MVC Application. Pin
Member 1476036418-Mar-20 21:19
Member 1476036418-Mar-20 21:19 
QuestionThanks for sharing this Pin
BuddhikaDinesh30-Aug-19 2:08
BuddhikaDinesh30-Aug-19 2:08 
QuestionInvalidOperationException: No service for type 'Owin.IAppBuilder' has been registered. Pin
Member 1304313119-Aug-19 2:01
Member 1304313119-Aug-19 2:01 
Questionimplement 2FA Pin
DevBassem21-May-18 16:42
DevBassem21-May-18 16:42 
QuestionIm getting { "error": "unsupported_grant_type" } Pin
buraczasek18-Feb-18 22:30
buraczasek18-Feb-18 22:30 
QuestionThe resource cannot be found. Pin
Member 1214312022-Oct-17 22:52
Member 1214312022-Oct-17 22:52 
I write my web api step by step with you. and i run this <a href="http://localhost:60523/api/Values"> in post man and its work very god. but when i run  http://localhost:60523/token  the response is The resource cannot be found. I try many time whit other url. but not work. please if possible connect my computer with teamviewer and help me. this is my mail: hnadi14@gmail.com  </a>[<a href="http://localhost:60523/api/Values" target="_blank" title="New Window">^</a>] 

AnswerRe: The resource cannot be found. Pin
pankajcode6-Sep-18 2:35
pankajcode6-Sep-18 2:35 
Questionhow to revoke access token from server side not client side Pin
Member 118986339-Oct-17 21:25
Member 118986339-Oct-17 21:25 
Questionhow to revoke access token from server side not client side Pin
Member 118986339-Oct-17 21:25
Member 118986339-Oct-17 21:25 
Questionrefresh access token Pin
Member 1189863324-Sep-17 2:30
Member 1189863324-Sep-17 2:30 
QuestionAccess token expiration time Pin
Member 1189863324-Sep-17 2:30
Member 1189863324-Sep-17 2:30 
QuestionFirst Request take about 30 second Pin
Member 1189863324-Sep-17 2:30
Member 1189863324-Sep-17 2:30 
AnswerRe: First Request take about 30 second Pin
aoh.correo6-Oct-17 16:05
aoh.correo6-Oct-17 16:05 
QuestionServer Error in '/' Application. The resource cannot be found. Pin
Member 804879228-Aug-17 22:22
Member 804879228-Aug-17 22:22 
AnswerRe: Server Error in '/' Application. The resource cannot be found. Pin
Abhijit S Kulkarni10-Sep-17 21:50
Abhijit S Kulkarni10-Sep-17 21:50 
Questionno source code include. Pin
Member 804879228-Aug-17 21:12
Member 804879228-Aug-17 21:12 
Questionhow to revoke access token Pin
Member 1189863324-Aug-17 22:02
Member 1189863324-Aug-17 22:02 
AnswerRe: how to revoke access token Pin
Abhijit S Kulkarni10-Sep-17 20:39
Abhijit S Kulkarni10-Sep-17 20:39 
GeneralRe: how to revoke access token Pin
Member 1189863316-Sep-17 13:13
Member 1189863316-Sep-17 13:13 
GeneralRe: how to revoke access token Pin
Abhijit S Kulkarni21-Sep-17 19:28
Abhijit S Kulkarni21-Sep-17 19:28 
GeneralRe: how to revoke access token Pin
Member 1189863324-Sep-17 1:24
Member 1189863324-Sep-17 1:24 
QuestionToken Based authentication for Admin,group, and the user.. Pin
NayeemMohammed24-Aug-17 0:16
NayeemMohammed24-Aug-17 0:16 
AnswerRe: Token Based authentication for Admin,group, and the user.. Pin
Abhijit S Kulkarni10-Sep-17 22:12
Abhijit S Kulkarni10-Sep-17 22:12 
QuestionVery Nice article ! Have you used Role based Authorization ? Pin
Paul Salvi15-Aug-17 21:40
Paul Salvi15-Aug-17 21:40 
QuestionNot working when selecting 'no authentication' Pin
Saranya Mahesh8-Aug-17 6:50
Saranya Mahesh8-Aug-17 6:50 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.