Hi folks,
Now we will discuss a very nice problem which generally happens when we use the Web API.
The problem is authorization and authentication for Web API resources.
Problem: We have a set of methods and a few of them are exposed to authenticated and registered applications (or you can say authenticated users) and the rest are for any others.
Solution
So let's start.
Step 1
Open the Visual Studio 2012.
"File" -> "New" -> "Project...".
Then chose "Web" under "Templates" depending on your familiar language, C# or VB.Net. I will choose C#.
Then set the name of the application and click OK.
Then select the "Web API" and "Razor" in the view engine.
Step 2
Now in the next step we need to create a delegating handler that will help to process the authenticated the request.
Right-click on the project then select "Add" -> "Class" then name it "ApplicationAuthenticationHandler.cs".
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace MonsterApiInc
{
public class ApplicationAuthenticationHandler
{
}
}
Then override the method "SendAsync".
public class ApplicationAuthenticationHandler : DelegatingHandler
{
protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
// Write your Authentication code here
return base.SendAsync(request, cancellationToken);
}
}
So I will write the following code to authenticate the application:
public class ApplicationAuthenticationHandler : DelegatingHandler
{
// Http Response Messages
private const string InvalidToken = "Invalid Authorization-Token";
private const string MissingToken = "Missing Authorization-Token";
protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
// Write your Authentication code here
IEnumerable<string> monsterApiKeyHeaderValues = null;
// Checking the Header values
if (request.Headers.TryGetValues("X-MonsterAppApiKey", out monsterApiKeyHeaderValues))
{
string[] apiKeyHeaderValue = monsterApiKeyHeaderValues.First().Split(':');
// Validating header value must have both APP ID & APP key
if (apiKeyHeaderValue.Length == 2)
{
// Code logic after authenciate the application.
var appID = apiKeyHeaderValue[0];
var AppKey = apiKeyHeaderValue[1];
if (appID.Equals("MosterIPhoneX123") && AppKey.Equals("ThisMonsterIsPersist"))
{
var userNameClaim = new Claim(ClaimTypes.Name, appID);
var identity = new ClaimsIdentity(new[] { userNameClaim }, "MonsterAppApiKey");
var principal = new ClaimsPrincipal(identity);
Thread.CurrentPrincipal = principal;
if (System.Web.HttpContext.Current != null)
{
System.Web.HttpContext.Current.User = principal;
}
}
else
{
// Web request cancel reason APP key is NULL
return requestCancel(request, cancellationToken, InvalidToken);
}
}
else
{
// Web request cancel reason missing APP key or APP ID
return requestCancel(request, cancellationToken, MissingToken);
}
}
else
{
// Web request cancel reason APP key missing all parameters
return requestCancel(request, cancellationToken, MissingToken);
}
return base.SendAsync(request, cancellationToken);
}
private System.Threading.Tasks.Task<HttpResponseMessage> requestCancel(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken, string message)
{
CancellationTokenSource _tokenSource = new CancellationTokenSource();
cancellationToken = _tokenSource.Token;
_tokenSource.Cancel();
HttpResponseMessage response = new HttpResponseMessage();
response = request.CreateResponse(HttpStatusCode.BadRequest);
response.Content = new StringContent(message);
return base.SendAsync(request, cancellationToken).ContinueWith(task =>
{
return response;
});
}
}
Step 3
Now to register the delegating handler.
Open the Global.asax file and provide one highlighted line of code.
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
GlobalConfiguration.Configuration.MessageHandlers.Add(new
ApplicationAuthenticationHandler());
}
}
Step 4
Make the resources authorized for the authenticated application (or users).
[Authorize]
Authorizing the attribute helps to make the resource available only for authorized applications and users.
Put "[Authorize]" before the method.
[Authorize]
public IEnumerable<string> Get()
{
return new string[] { "MonsterValue1", "MonsterValue2" };
}
If the method does not have "[Authorize]" then it can be used by anonymous request.
Or we can use the "[AllowAnonymous]" attribute for anonymous requests.
namespace MonsterApiInc.Controllers
{
public class ValuesController : ApiController
{
// GET api/values
[Authorize]
public IEnumerable<string> Get()
{
return new string[] { "MonsterValue1", "MonsterValue2" };
}
// GET api/values/5
public string Get(int id)
{
return "MonsterValue";
}
// POST api/values
public void Post([FromBody]string value)
{
}
// PUT api/values/5
public void Put(int id, [FromBody]string value)
{
}
// DELETE api/values/5
public void Delete(int id)
{
}
}
}
Now it's testing time.
Open the Fiddler Tool.
Download Fiddler here.
Build and run the project.
Then open Fiddler and supply the WebAPI resource URL and click "Execute" without header value (AppID and App Key) .
Because we have not supplied the application authentication token we get the Bad Request response that we have set when no header is found.
// Web request cancel reason APP key missing all parameters
return requestCancel(request, cancellationToken, MissingToken);
private System.Threading.Tasks.Task<HttpResponseMessage> requestCancel(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken, string message)
{
CancellationTokenSource _tokenSource = new CancellationTokenSource();
cancellationToken = _tokenSource.Token;
_tokenSource.Cancel();
HttpResponseMessage response = new HttpResponseMessage();
response = request.CreateResponse(HttpStatusCode.BadRequest);
response.Content = new StringContent(message);
return base.SendAsync(request, cancellationToken).ContinueWith(task =>
{
return response;
});
}
Now let's the valid authentication token
Then Execute and you will the response.
Go to the left pane and select the response.
Now we can get the response.
If you have any questions then please post the question here, I will try to provide a quick response.
Enjoy !!!