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)
- {
-
- return base.SendAsync(request, cancellationToken);
- }
- }
So I will write the following code to authenticate the application:
- public class ApplicationAuthenticationHandler : DelegatingHandler
- {
-
- 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)
- {
-
- IEnumerable<string> monsterApiKeyHeaderValues = null;
-
- if (request.Headers.TryGetValues("X-MonsterAppApiKey", out monsterApiKeyHeaderValues))
- {
- string[] apiKeyHeaderValue = monsterApiKeyHeaderValues.First().Split(':');
-
- if (apiKeyHeaderValue.Length == 2)
- {
-
- 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
- {
-
- return requestCancel(request, cancellationToken, InvalidToken);
- }
- }
- else
- {
-
- return requestCancel(request, cancellationToken, MissingToken);
- }
- }
- else
- {
-
- 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
- {
-
- [Authorize]
- public IEnumerable<string> Get()
- {
- return new string[] { "MonsterValue1", "MonsterValue2" };
- }
-
- public string Get(int id)
- {
- return "MonsterValue";
- }
-
- public void Post([FromBody]string value)
- {
- }
-
- public void Put(int id, [FromBody]string value)
- {
- }
-
- 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.
-
- 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 !!!