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 web method'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_Start
method.
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();
GlobalConfiguration.Configuration.MessageHandlers.Add(basicAuthMessageHandler);
}