Tuesday, June 23, 2015

Web API Authentication, Authorization


Forms Authentication using Web API


If using Identity framework is not possible, one can add the old school forms authentication method.

Introduction

With the advent of OWIN middleware and Identity framework, traditional forms authentication is outdated since OWIN middleware and Identity framework takes care of everything in a better and organized manner. But sometimes, existing applications cannot be migrated to Identity framework due to one or the other reason, but Form Authentication user login is needed. For such situations, here is the workaround.

Background

There was an existing ASP.NET application using role based forms authentication, and was supposed to be migrated to ASP.NET MVC, but for some reason, the client wanted to stick to forms authentication.

Using the Code

To implement forms authentication, interception of both request and response is required which is done with the help of DelegatingHandler.
public class BasicAuthMessageHandler : DelegatingHandler
    {
        private const string BasicAuthResponseHeader = "WWW-Authenticate";
        private const string BasicAuthResponseHeaderValue = "Basic";

        public adminPrincipalProvider PrincipalProvider = new adminPrincipalProvider();

        protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(
            HttpRequestMessage request,
            CancellationToken cancellationToken)
        {
            AuthenticationHeaderValue authValue = request.Headers.Authorization;
            if (authValue != null && authValue.Parameter != "undefined" && 
    !String.IsNullOrWhiteSpace(authValue.Parameter))
            {
                string email = authValue.Parameter;
                if (HttpContext.Current.Session == null || 
  HttpContext.Current.Session["userToken"] == null || 
  string.IsNullOrEmpty(HttpContext.Current.Session["userToken"].ToString()))
                {
                    HttpContext.Current.Session["userToken"] = email;
                }
                else
                {
                    email = HttpContext.Current.Session["userToken"].ToString();
                }

                if (!string.IsNullOrEmpty(email))
                {
                    IPrincipal principalObj = PrincipalProvider.createPrincipal(email, "Admin");
                    Thread.CurrentPrincipal = principalObj;
                    HttpContext.Current.User = principalObj;
                }
            }
            return base.SendAsync(request, cancellationToken)
               .ContinueWith(task =>
               {
                   var response = task.Result;
                   if (response.StatusCode == HttpStatusCode.Unauthorized
                       && !response.Headers.Contains(BasicAuthResponseHeader))
                   {
                       response.Headers.Add(BasicAuthResponseHeader
                           , BasicAuthResponseHeaderValue);
                   }
                   return response;
               });
        }
    }
Principal object is used to assign role to a validated user, this principal object is added to HttpContext's user property.

Controller Login & Logout Web Method

       [HttpPost, AllowAnonymous, Route("login")]
        public async Task<HttpResponseMessage> Login([FromBody]LoginRequest request)
        {
            var loginService = new LoginService();
            LoginResponse response = await loginService.LoginAsync(request.username, request.password);
            if (response.Success)
            {
                FormsAuthentication.SetAuthCookie(response.Token, false);
            }
            return Request.CreateResponse(HttpStatusCode.OK, response);
        }

        [HttpPost, AllowAnonymous, Route("logout")]
        public void Signout()
        {
            FormsAuthentication.SignOut();

            if (HttpContext.Current.Session != null)
                HttpContext.Current.Session.Abandon();
        }
To setup role based authorization on webmethods, the following attributes are to be added on the top of webmethod's implementation.
[HttpGet, Authorize(Roles = "admin"), Route("name")]

Calling from Client

The following jquery code shows how to make a Login call.
$(document).ready(
    function () {
       $("#btnSubmit").click(function () {
          var usrname = $("#username").val();
          var pwd = $("#password").val();
          $.post("http://localhost:50750/api/loginctrl/login", 
  { username: usrname, password: pwd }, function (result) {
                    alert(JSON.stringify(result));
                });
                       
          });
        });

Registering Delegating Handler

Before starting the execution of the project, custom Delegating Handler is to be registered with Application's Message Handlers. In this app, Delegating handler is registered inside Global.asax's Application_Startmethod.
 protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            GlobalConfiguration.Configure(WebApiConfig.Register);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);

            var basicAuthMessageHandler = new WebAPI_FormsAuth.Helper.BasicAuthMessageHandler();
            basicAuthMessageHandler.PrincipalProvider = 
   new WebAPI_FormsAuth.Helper.adminPrincipalProvider();
            //start message handler
            GlobalConfiguration.Configuration.MessageHandlers.Add(basicAuthMessageHandler);
        }




http://weblog.west-wind.com/posts/2013/Apr/18/A-WebAPI-Basic-Authentication-Authorization-Filter

No comments: