1
votes

I have implemented Forms authentication in one project. In the forms authentication cookie I store the Login Id of the user. i.e.

FormsAuthentication.SetAuthCookie(LoginId, false); 

I now need to read the cookie value on every request to get more information about the user and put this information in the HttpContext.Items property. The project is a MVC project that has both regular MVC Controllers as well as Web API controllers. Currently I have created two action filters - one for the MVC controllers and other for Web API Controllers where I read this value. So like

public class MyMvcFilter : AuthorizeAttribute 
{
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        var cookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
        if (cookie != null)
        {
            var ticket = FormsAuthentication.Decrypt(cookie.Value);
            filterContext.HttpContext.Items.Add("LoginId",ticket.Name);
        }


        base.OnAuthorization(filterContext);
    }
}

and

public class MyFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        base.OnActionExecuting(actionContext);

        var cookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
        if (cookie == null)
        {
            return;
        }
        var ticket = FormsAuthentication.Decrypt(cookie.Value);
        if (ticket == null)
        {
            return;
        } 

        actionContext.Request.Properties.Add("LoginId", userId);

    }
}

However the more I think of it, the more it looks like an ugly hack to me. What would be correct location where I can decrypt the authentication cookie and remains the same for MVC controller as well as Web API controller ?

2

2 Answers

1
votes

I would:

  1. Read the cookie on the

    protected void Application_PostAuthenticateRequest(object sender, EventArgs e)
    {
    
    }
    
  2. In the above method....Convert the cookie to a ClaimsPrincipal. Like the below:

    protected void Application_PostAuthenticateRequest(object sender, EventArgs e)
    {
    
    
    
       IList<Claim> claimCollection = new List<Claim>
        {
            new Claim("http://www.mycompany.com/claims/LoginId, "123456" /* use info from cookie instead of 123456*/)
        };
    
        ClaimsIdentity claimsIdentity = new ClaimsIdentity(claimCollection, "My e-commerce website");
    
        Console.WriteLine(claimsIdentity.IsAuthenticated);
    
        ClaimsPrincipal customPrinc = new ClaimsPrincipal(claimsIdentity);
    
        if (null != customPrinc)
        {
            Thread.CurrentPrincipal = customPrinc; /* Set here.  But when you need to "get" it, use "System.Security.Claims.ClaimsPrincipal.Current" */
            /* ASP.NET Authorization depends on value of HttpContext.Current.User.
             * Consider putting ClaimsPrincipal  into both  HttpContext.Current.User and Thread.CurrentPrincipal */
            HttpContext.Current.User = customPrinc; 
    
           /* Note the second setter is necessary so you don't lose it later on, learned the hard way by experience */
        }
    }
    

Retrieve the Claims Principal as needed..

    /* MVC */
    public override void OnAuthorization(System.Web.Mvc.AuthorizationContext filterContext)
    {
        /* the below should be what you set in the Application_PostAuthenticateRequest method */
        IPrincipal currentClaimsPrinc = ClaimsPrincipal.Current;

    }


    /* WebAPI */
    public override void OnAuthorization(HttpActionContext actionContext)
    {
        IPrincipal claimsPrincCurrent = ClaimsPrincipal.Current;

    }

You could also do this:

adding claims to forms authentication in asp.net

or this:

http://brockallen.com/2013/01/26/replacing-forms-authentication-with-wifs-session-authentication-module-sam-to-enable-claims-aware-identity/

or this:

http://chris.59north.com/post/Claims-based-identities-in-ASPNET-MVC-45-using-the-standard-ASPNET-providers

1
votes

MVC and WebAPI (until .NET Core) do not share the same pipeline so you need to use two different filters.

What you can do is share the code, if you want, maybe with an utility method or something. Just to avoid having two codes doing the same stuff