Introduction
This article will provide some guidelines on how to integrate ApiFrame into ASP.NET Web API project. ApiFrame is a simple .NET library that provides support for implementing Web API security (HMAC Authentication), exception handling, and versioning of Web API methods. The library is like a plugin-component that you can easily integrate into your application. You will find the source code and information related to ApiFrame at this code project article.
ApiFrame : A simple library for Web API security, exception and versioning[^]
I will walk you through a simple Web API sample application that implements POST and GET method for demonstrating Authentication, Authorization, Versioning and Exception Handling. Also, I will take you through how to consume the service from an API Client.
Guidelines: How to integrate API Frame in Web API Project
First, Let us create a sample application.
- Open Visual Studio 2012
- Create a new ASP.NET MVC 4 Web Application
- Select Web API project template
Installation
To install ApiFrame, run the following command in the Package Manager Console
Alternate, the library can be referenced in your project through Nuget Package Manager. In the solution explorer, Select the Web API project -> Right click and Open “Manage Nuget Packages…” -> Search for “ApiFrame” online. You will get the following and Click the install button, this will add API frame to your project.
Configuration
If you are implementing Authentication and Authorization through ApiFrame, then it is required to implement the following interface in your application.
For demo purpose let’s do the following in the Web API Project
- Create a new folder named “ApiFrameConfig”
- Inside the new folder
- Create a class “ApiInception” that Implements “IApiInception”
- Create a class “ApiException” that implements “IApiException”
Implementing IApiInception
This interface exposes three methods. The implementation of GetApplicationToken()in you project depends on your business model. If you are exposing the API service to a number of customers then it is required to have an access token and a secret token attached to each customer. Generally, access token and secret token is created at the time of registration and the same is share with the customer for integrating the client application to communicate with the service. If the service is exposed to a single customer then this configuration can be added to Web.config file. AuthScheme is the Authorization Scheme, this can be a common label that you keep it Web.Config or if you would like to attach an abbreviated name for each of you customer then you can use this property. It’s up to your choice but this piece of information is also validated by the library. So, AuthScheme is another piece of information you will need to share with the client.
For the demo purpose, I have the following Implementation in my sample.
public ApiApplicationToken GetApplicationToken(string accessToken)
{
return new ApiApplicationToken()
{
AuthScheme = "HMAC",
AccessToken = "d85aa01c-cf92-4f1e-9862-638fa4ae37b4",
SecretToken = "06fb3823-b8cc-462b-b855-f655d49eb3e2"
};
}
Authentication is based on username and password. Ideally, the below method in your project will call the service/data access layer to check if the given user name and password is valid. For implementing this method, an access token and a secret token should be attached to each existing user. If the username and password is valid then the method should returns a user token as shown in the code sample below. This user tokens are used for authorization purpose. It’s up to the client application to preserve the tokens and use it for issuing a subsequent request to access an authorized resource. ApiFrame supports Role based access in addition. The Role property can be empty if you application doesn’t use Roles based access.
The below code is for demo purpose.
public ApiUserToken GetUserToken(string username, string password)
{
if (username == "demo" && password == "demo123")
{
return new ApiUserToken()
{
AuthScheme = "HMAC",
AccessToken = "94d05acb-54de-4e39-9303-3764cabcaf18",
SecretToken = "88a10072-5fa9-4a8d-b9ac-42966d8da4bc",
Name = "DemoUser",
UserId = "001",
Roles = "Users"
};
}
return null;
}
The below method is for Authorization. ApiFrame calls this method on authorization by passing the access token of a user as a input parameter to the method. In the real application, this method should call a service or data access method to query the database to fetch the user details by access token. The method constructs a ApiUserToken with the user details and returns the token.
public ApiUserToken GetUserToken(string accessToken)
{
if (accessToken == "94d05acb-54de-4e39-9303-3764cabcaf18")
{
return new ApiUserToken()
{
AuthScheme = "HMAC",
AccessToken = "94d05acb-54de-4e39-9303-3764cabcaf18",
SecretToken = "88a10072-5fa9-4a8d-b9ac-42966d8da4bc",
Name = "DemoUser",
UserId = "001",
Roles = "Users"
};
}
return null;
}
Implementing IApiException
Exceptions can be caught by implement this interface.
public class ApiException : IApiException
{
public void LogMessage(Exception exception)
{
throw new NotImplementedException();
}
}
Once, you have done implementing the interfaces, it is required to add the following configuration in the project to injects the implementation (dependencies) into the library. In the Global.asax file, Include the below code in the Application_Start()
ApiObjectFactory.RegisterType<IApiInception, ApiInception>();
ApiObjectFactory.RegisterType<IApiException, ApiException>();
ApiObjectFactory.RegisterType<IApiSignature, ApiSignature>();
Sample Web API methods
In you are interested in versioning you Web API methods, ApiFrame provides an option for versioning using MVC Areas or using namespace. Let’s try using Areas.
Create a new MVC Area and name it as “V1” and do the following configuration in WebApiConfig.cs file. That it’s, you are almost done with setting up versioning your web API methods and rest you will have to play with the routing.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Services.Replace(typeof(IHttpControllerSelector), new ApiControllerSelector(config));
}
}
Create the following model in V1
public class AccessModel
{
public string AccessToken { get; set; }
public string SecretToken { get; set; }
}
Add a new controller to the project and name it “DemoController”. Frame the DemoController with the “ApiException” attribute to allow ApiFrame to handle the exception for you.
[ApiException]
public class DemoController : ApiController
{
}
Add the following methods to the DemoController. Let’s create the following methods for demo.
- SignIn - POST method for Authentication demo
- Relative route : /demo/signin
- GetMessage - GET Method for Authorization demo
- Relative route : /demo/getmessage
You will notice that the below SignIn method is framed with ApiAuthentication filter attribute. Similarly, GetMessage with ApiAuthorization filter attribute. This filter attributes handles the authentication and authorization by calling the injected methods implemented by IApiInception.
[HttpPost]
[ApiAuthentication]
public AccessModel SignIn()
{
return new AccessModel
{
AccessToken = "94d05acb-54de-4e39-9303-3764cabcaf18",
SecretToken = "88a10072-5fa9-4a8d-b9ac-42966d8da4bc"
};
}
[HttpGet]
[ApiAuthorization(Roles = "Users")]
public string GetMessage()
{
string user = ((ApiIdentity)HttpContext.Current.User.Identity).Name;
return string.Format("Server Message :This is an Authorized Request - LoggedIn User:{0}", user);
}
Update the default route in V1AreaRegistration class.
public override void RegisterArea(AreaRegistrationContext context)
{
context.Routes.MapHttpRoute(
"V1_default",
"{area}/{controller}/{action}/{id}",
new { id = UrlParameter.Optional }
);
}
Run the application and you will see the below screen. My sample Web API service is running at URL localhost:6360
The API client should use the same URL to communicate with the service.
Sample API client
For the demo purpose, let’s consume the Web API service from a console application.
- Open Visual Studio 2012
- Create a Console Application
- Add reference to System.Net.Http assembly
- Install the following through Nuget Packet Manager
We need the following initial settings before we call the API service
string serviceUrl = "http://localhost:6360/";
string apiVersion = "v2";
ApiObjectFactory.RegisterType<IApiSignature, ApiSignature>();
ApiFrame uses HMAC-SHA256 algorithm to calculate the signature. Same algorithm need to use in both ends (client and server). For this we need the above signature configuration in client.
Server will identify the client with the token and the following configuration is required and the tokens should be the same as what is stored in the server.
const string AuthScheme = "HMAC";
const string AccessToken = "d85aa01c-cf92-4f1e-9862-638fa4ae37b4";
const string SecretToken = "06fb3823-b8cc-462b-b855-f655d49eb3e2";
Follow the steps to check the SignIn API service
- Read the username and password through the console
- Create a post data with Username and Password
- Create the request token with the required parameters for the service call
- Create an instance of ApiClientGateway by passing the service url
- Call the Execute method of ApiClientGateway by passing the request token
Console.WriteLine("\n Authentication -> Sign-in demo - START");
Console.Write("\n\n Enter the Username:");
string userName = Console.ReadLine();
Console.Write(" Enter the Password:");
string password = Console.ReadLine();
string postData = string.Format("Username={0}&Password={1}", userName, password);
ApiRequestToken requestToken = new ApiRequestToken()
{
Verb = HttpMethod.Post,
RelativeUrl = string.Format("{0}/demo/signin", apiVersion),
AccessToken = AppAccessToken,
SecretToken = AppSecretToken,
AuthScheme = AuthScheme,
Content = postData
};
ApiClientGateway apiClientGateway = new ApiClientGateway(serviceUrl);
AccessModel signInResult = null;
try
{
signInResult = apiClientGateway.Execute<AccessModel>(requestToken);
if (signInResult != null)
{
Console.WriteLine("\n Response from server");
Console.WriteLine("\n AccessToken:" + signInResult.AccessToken);
Console.WriteLine(" SecretToken:" + signInResult.SecretToken);
Console.WriteLine("\n Successfully Authenticated! This is ApiAuthentication Test");
}
}
catch (ApiRequestException ex)
{
Console.WriteLine(ex.ErrorResponseMessage.
Content.ReadAsStringAsync().Result);
}
When calling the sign-in service, the server returns a token (Access token and secret token) on successful authentication. This token is preserved and used for subsequent service call. Below is the sample that call an authorized API service method that return a private message. Let’s the use the received token to make a next service call to get the private message
Console.WriteLine("\n Authorization demo -> GetMessage - START");
Console.Read();
if (signInResult != null)
{
requestToken = new ApiRequestToken()
{
Verb = HttpMethod.Get,
RelativeUrl = string.Format("{0}/demo/GetMessage", apiVersion),
AccessToken = signInResult.AccessToken,
SecretToken = signInResult.SecretToken,
AuthScheme = AuthScheme
};
string messageResult = string.Empty;
try
{
messageResult = apiClientGateway.Execute(requestToken);
if (!string.IsNullOrEmpty(messageResult))
{
Console.WriteLine("\n This is ApiAuthorization test");
Console.WriteLine("Server Response :" + messageResult);
}
}
catch (ApiRequestException ex)
{
Console.WriteLine(ex.ErrorResponseMessage.
Content.ReadAsStringAsync().Result);
}
}
Console.Read();
On exception, service returns an exception of type ApiRequestException. Below shown is the screen shot of service call for signin with wrong credential.
catch (ApiRequestException ex)
{
Console.WriteLine(ex.ErrorResponseMessage.
Content.ReadAsStringAsync().Result);
}
API Versioning demo
In the Web API project, Let’s create a new version of “DemoController” and add new version of Sign-In and GetMessage service.
- Create a new MVC Area named “V2”
- Add a new “DemoController” and “AccessModel”
- Copy the SignIn and GetMessage service from version V1
- Update the GetMessage service to contain a new version of message
- Run the application and keep the service up and running
In the API client, Update the apiVersion and run the application. The authorized service "GetMessage" will now return a new version of updated message.