Client Validation Using Basic Authentication in Web API

In this article, I am going to discuss how to implement Client Validation Using Basic Authentication in Web API. Please read our Token Based Authentication in Web API article before proceeding to this article as we are going to work the same example.

I think you have checked my Token Based Authentication in Web API where I used a class MyAuthorizationServerProvider like this

The first method i.e. ValidateClientAuthentication method is responsible for validating the Client, in the Token Based Authentication in Web API example, we assume that we have only one client so we’ll always return that it is validated successfully.

Assume that we have more than one client, who is going to consume our service. In such a case, we need to validate the clients within the ValidateClientAuthentication method. For that we have to create a table in the database ClientMaster table. Script is written below:


-- Create ClientMaster table
CREATE TABLE ClientMaster
(
  ClientKeyId INT PRIMARY KEY IDENTITY,
  ClientId VARCHAR(500),
  ClientSecret VARCHAR(500),
  ClientName VARCHAR(100),
  CreatedOn DateTime
)
GO

-- Populate the ClientMaster with test data
 INSERT INTO ClientMaster(ClientId, ClientSecret, ClientName, CreatedOn) 
 VALUES(NEWID(), NEWID(), 'Client1', GETDATE())

 INSERT INTO ClientMaster(ClientId, ClientSecret, ClientName, CreatedOn) 
 VALUES(NEWID(), NEWID(), 'Client2', GETDATE())

 INSERT INTO ClientMaster(ClientId, ClientSecret, ClientName, CreatedOn) 
 VALUES(NEWID(), NEWID(), 'Client3', GETDATE())

After creating the table update the Edmx file.

After that Create a class file with the name ClientMasterRepository and write the below code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace TokenAuthentication
{
    public class ClientMasterRepository : IDisposable
    {
        // SECURITY_DBEntities it is your context class
        Authentication_DBEntities context = new Authentication_DBEntities();

        //This method is used to check and validate the Client credentials
        public ClientMaster ValidateClient(string ClientID, string ClientSecret)
        {
            return context.ClientMasters.FirstOrDefault(user =>
             user.ClientId == ClientID
            && user.ClientSecret == ClientSecret);
        }
        public void Dispose()
        {
            context.Dispose();
        }
    }
}
  

Here we create the ValidateClient method . It’s the ClientID and ClientSecret as input parameter and checks in the ClientMaster table whether the client is valid or not and returns the client details.

Now we need to modify the ValidateClientAuthentication() method of MyAuthorizationServerProvider class like below code:

using Microsoft.Owin.Security.OAuth;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Web;

namespace TokenAuthentication
{
    public class MyAuthorizationServerProvider : OAuthAuthorizationServerProvider
    {
        public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
        {
            string clientId = string.Empty;
            string clientSecret = string.Empty;
            // The TryGetBasicCredentials method checks the Authorization header and Return the ClientId and clientSecret
            if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
            {
                context.SetError("invalid_client", "Client credentials could not be retrieved through the Authorization header.");
                context.Rejected();
                return;
            }
            //Check the existence of by calling the ValidateClient method
            ClientMaster client = (new ClientMasterRepository()).ValidateClient(clientId, clientSecret);
            if (client != null)
            {
                // Client has been verified.
                context.OwinContext.Set<ClientMaster>("oauth:client", client);
                context.Validated(clientId);
            }
            else
            {
                // Client could not be validated.
                context.SetError("invalid_client", "Client credentials are invalid.");
                context.Rejected();
            }
            context.Validated();
        }

        public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {
            using (UserMasterRepository _repo = new UserMasterRepository())
            {
                var user = _repo.ValidateUser(context.UserName, context.Password);
                if (user == null)
                {
                    context.SetError("invalid_grant", "Provided username and password is incorrect");
                    return;
                }
                var client = context.OwinContext.Get<ClientMaster>("oauth:client");
                var identity = new ClaimsIdentity(context.Options.AuthenticationType);
                identity.AddClaim(new Claim(ClaimTypes.Role, user.UserRoles));
                identity.AddClaim(new Claim(ClaimTypes.Name, user.UserName));
                identity.AddClaim(new Claim("Email", user.UserEmailID));
                identity.AddClaim(new Claim("ClientName", client.ClientName));
                identity.AddClaim(new Claim("ClientID", client.ClientKeyId));
                identity.AddClaim(new Claim("ClientSecret", client.ClientSecret));
                context.Validated(identity);
            }
        }
    }
}

We need to pass the ClientId and ClientSecret using the Basic authentication in the authorization header i.e. in Base64 encoded format.

Now change the action method like beow code to get the Client details:

 [Authorize(Roles = "SuperAdmin, Admin, User")]
        [HttpGet]
        [Route("api/Auth/resource1")]
        public IHttpActionResult Get1()
        {
            var identity = (ClaimsIdentity)User.Identity;
            var ClientID = identity.Claims.FirstOrDefault(c => c.Type == "ClientID").Value;
            var ClientName = identity.Claims.FirstOrDefault(c => c.Type == "ClientName").Value;
            var ClientSecret = identity.Claims.FirstOrDefault(c => c.Type == "ClientSecret").Value;
            return Ok("Hello: " + identity.Name + ", Client Name is =" + ClientName);
        }

Before testing the APi we have to first create the Base64 Encode value by the ClientID and ClientSecret using this link.

Enter the ClientID and ClientSecret separated by a colon (:) in “Encode to Base64 format” textbox, and then click on the “Encode” button as shown in the below diagram which will generate the Base64 encoded value.

After getting the Encoded data we call the APi in POSTMAN as shown below:

Select the Method as POST and provide URI and select the Header tab and provide the Authorization value as shown below.

Authorization: BASIC QTJGNzJCOTMtMUY4NC00RTBBLUE4NTItMEQzMEFBOEUzRENGOkRFN0M2QTVFLUE4OUQtNEVEMi1CODU4LTJFQURFN0FCRjQ3NQ==

Then Select the Body Tab. Then choose x-www-form-urlencoded option and provide the username and password value. Provide the grant_type value as password as shown in the below image,

After clicking the send button you will get the access token.

Now use that access token in the resource1 Api and get the desired result.

0 0 vote
Article Rating

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x