Back to: ASP.NET Web API Tutorials For Begineers and Professionals
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 previous article before proceeding to this article as we are going to work the same example. In our last article, we discussed how to implement Token Based Authentication in ASP.NET Web API.
If you observed in the last article, we have created the following MyAuthorizationServiceProvider class.
The first method i.e. ValidateClientAuthentication method is responsible for validating the Client, in the above example, we assume that we have only one client so we’ll always return that it is validated successfully.
Let’s change the requirement. 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.
Let’s see how to achieve this.
For this, we are going to use the following ClientMaster table
Please use below SQL Script to create and populate the ClientMaster table with some test data.
USE SECURITY_DB GO -- 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(), 'My Client1', GETDATE()) INSERT INTO ClientMaster(ClientId, ClientSecret, ClientName, CreatedOn) VALUES(NEWID(), NEWID(), 'My Client2', GETDATE()) INSERT INTO ClientMaster(ClientId, ClientSecret, ClientName, CreatedOn) VALUES(NEWID(), NEWID(), 'My Client3', GETDATE())
Once you create the ClientMaster table, then you need to update the EDMX file to add the above ClientMaster table.
Create a class file with the name ClientMasterRepository.cs and then copy and paste the following code.
namespace TokenAuthenticationInWebAPI.Models { public class ClientMasterRepository : IDisposable { // SECURITY_DBEntities it is your context class SECURITY_DBEntities context = new SECURITY_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 which is very straightforward. It’s the ClientID and ClientSecret as input parameter and checks in the ClientMaster table whether the client is valid or not and it simply returns the client details.
Now we need to modify the ValidateClientAuthentication() method of MyAuthorizationServerProvider class as shown below.
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(); }
Note: We need to pass the ClientId and ClientSecret using the Basic authentication in the authorization header i.e. in Base64 encoded format.
Modify the GetResource1 action method of the TestController as shown below.
Testing the API using Postman:
Let’s first create the Base64 Encode value by for the ClientID and ClientSecret by using the following website
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.
Once you generate the Base64 encoded string, let’s see how to use basic authentication in the header to pass the Base64 encoded value.
Here we need to use the Authorization header and the value will be the Base64 encoded string followed the “BASIC” as shown below.
Authorization: BASIC QzFBMDNCMTAtN0Q1OS00MDdBLUE5M0UtQjcxQUIxN0FEOEMyOjE3N0UzMjk1LTA2NTYtNDMxNy1CQzkxLUREMjcxQTE5QUNGRg==
Let’s see step by step procedure to use the Postman to generate the Access Token
Step1:
Select the Method as POST and provide URI as shown below in the below image
Step2:
Select the Header tab and provide the Authorization value as shown below.
Authorization: BASIC QzFBMDNCMTAtN0Q1OS00MDdBLUE5M0UtQjcxQUIxN0FEOEMyOjE3N0UzMjk1LTA2NTYtNDMxNy1CQzkxLUREMjcxQTE5QUNGRg==
Step3:
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,
Now click on the Send button which will generate the access token as shown below.
Once the access token is generated, we use that token to access the resources as shown below.
In the next article, I will discuss how to generate Refresh Token in ASP.NET Web API. Here, in this article, I try to explain how to implement Client Validation Using Basic Authentication in Web API with an example. I hope this article will help you with your need. I would like to have your feedback. Please post your feedback, question, or comments about this article.
Hi, I’ve followed this post and the previous ones, and all was ok but in this one, the Test Resource1 modified as required is not working.
The Token method works and gives back the Token but the Claims contain only the Username and Password, they do not contain ClientID, ClientSecret, ClientName so maybe I’m doing something wrong in the Client validation that is not setting the Client Data into the claims list.
Do you have any suggestion?
Thanks in advance
I’ve found the answer to my previous question, before using the test method it is necessary to add the properties from the Client data to the Claims of the context. My solution is the following:
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
UsersRepository _repo = new UsersRepository();
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(“oauth:client”);
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Role, user.GetRolesString()));
identity.AddClaim(new Claim(ClaimTypes.Name, user.UserName));
identity.AddClaim(new Claim(“Email”, user.Email));
identity.AddClaim(new Claim(“FullName”, string.Format(user.FullName)));
identity.AddClaim(new Claim(“FullLogin”, user.ToJson()));
identity.AddClaim(new Claim(“ClientName”, client.ClientName));
identity.AddClaim(new Claim(“ClientKeyID”, client.ClientKeyID));
identity.AddClaim(new Claim(“ClientKeySecret”, client.ClientKeySecret));
context.Validated(identity);
}
This way the properties of the client can be shown in the resource1 method. You can also put the whole class in a claim using a json serialization as I did in my user class.
HTH
Sabrina
Thank you for your idea. It works for me. But I don’t know where is your UsersRepository come from? I only have UserMasterRepository and ClientMasterRepository. However, based on your idea. I add the clientID and ClientName to identity and finally, it works.
Hi,
How to update .edmx file. below suggestion are not working
Suggestion 1:
1.Build the project after updating EDMX file.
2.Right click your .tt file in solution explorer.
3.Select “Run Custom Tool” option
Suggestion 2:(on right-click not getting any option like “Update Model from Database”)
In the Model Browser, right-click the .edmx file and select Update Model from Database.
Expand the Tables, Views, and Stored Procedures nodes, and check the objects you want to add to the .edmx file.
I cant get this to work. the var identity = (ClaimsIdentity)User.Identity;
var ClientID = identity.Claims
.FirstOrDefault(c => c.Type == “ClientID”).Value;
// this line always has a null value.
var ClientID = identity.Claims
.FirstOrDefault(c => c.Type == “ClientID”).Value;
// this line always has a null value.
My Solution:
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(“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(“ClientId”,client.ClientId));
identity.AddClaim(new Claim(“ClientName”,client.ClientName));
identity.AddClaim(new Claim(“ClientSecret”,client.ClientSecret));
context.Validated(identity);
}
}
var ClientID = identity.Claims.FirstOrDefault(c => c.Type == “ClientID”).Value;
// this line always has a null value. I got the same error as well
Tried to follow GKHN suggestion, but I I get an error at the following line when I try to update the GrantResourceOwnerCredentials
var client = context.OwinContext.Get(“oauth:client”);
// The type arguments for method ‘IOwinContext.Get(string)’ cannot be inferred from the usage.
Any helps or tips is highly appreciated.
Use
var client = context.OwinContext.Get(“oauth:client”);
Try
var client = context.OwinContext.Get(“oauth:client”);
This article shows how to authenticate a user. As the user works in the system updating and adding new records, how can we get the user info to add “CreatedBy” or “Updatedby” fields of out tables?
var ClientID = identity.Claims.FirstOrDefault(c => c.Type == “ClientID”).Value;
// this line always has a null value. I got the same error as well
Tried to follow GKHN suggestion, but I I get an error at the following line when I try to update the GrantResourceOwnerCredentials
var client = context.OwinContext.Get(“oauth:client”);
// The type arguments for method ‘IOwinContext.Get(string)’ cannot be inferred from the usage.
Any helps or tips is highly appreciated.
var client = context.OwinContext.Get(“oauth:client”);//your code to be changed
The solution is to change the above line of code to:
var client = context.OwinContext.Get(“oauth:client”);
change to:
var client = context.OwinContext.Get(“oauth:client”)
it works!
var client = context.OwinContext.Get[ClientMaster](“oauth:client”)
[ClientMaster]:
change [ and ] to “less then” sign and “greater then” sign.
var client = context.OwinContext.Get(“oauth:client”);
var client = context.OwinContext.Get(“oauth:client”);
T=client
try
var client = context.OwinContext.Get(“oauth:client”);
var client = context.OwinContext.Get””(“oauth:client”);
remove ” ” outside the T
the comment removed my generic type. after GEt you need have a generic type of ClientMaster, to convert oauth:client to ClientMaster type
also make sure it is ClientId not ClientID, the screen copy provided by Author is ClientID but in the database field is ClientId so the ClientMaster uses ClientId, it is case sensitive. C# cannot map them
Can you help me here as I am getting error in postman as -> “error”: “invalid_client”
Can someone pls help me here as I am getting error in postman as -> “error”: “invalid_client”