0
votes

I'm developing Windows-authentication claims-based MVC application. I've implemented the IHttpModule (aka "ClaimsTransformation Module", which interceps the Identity) and custom ClaimsAuthenticationManager (which adds additional claims to this identity), as shown below. Page loads, and I can retrieve newly added claim, but there few serious issues...

The problems are:

  1. Even on initial page load my custom RomesClaimsAuthenticationManager.Authenticate method gets called 27+ times (I assume some calls are parrallel/async).
  2. The FedAuth (SessionToken) cookie check never returns true, even though right after SAM (SessionAuthenticationManager) writes SesstionToken to cookie - I can see it, but at the next call (still during original page load) it's gone - same thing happens if I open other pages.
    
public class RomesClaimsAuthorizationModule : IHttpModule, IDisposable
{

    public void Init(System.Web.HttpApplication application)
    {
        // intercept PostAuthenticationRequest to add custom logic
        application.PostAuthenticateRequest += TransformPrincipal;
    }

    private static void TransformPrincipal(object sender, EventArgs e)
    {

        var context = ((HttpApplication)sender).Context;

        // PROBLEM HERE - this is always false, even after cookie has been set
        // check if cookie with auth info about curr user already exists
        if (FederatedAuthentication.SessionAuthenticationModule != null && 
            FederatedAuthentication.SessionAuthenticationModule.ContainsSessionTokenCookie(HttpContext.Current.Request.Cookies))
        {
            return;
        }
        else
        {
            if (HttpContext.Current.User.Identity.IsAuthenticated)
            {
                // this will pick up our custom Romes.Adminn.Security.RomesClaimsAuthenticationManager
                // it is specified in web.config, so our app will use it as default
                // which will add our custom additional claims to our principal
                var transformer = FederatedAuthentication.FederationConfiguration.IdentityConfiguration.ClaimsAuthenticationManager;
                if (transformer != null)
                {                    
                    var transformedPrincipal = transformer.Authenticate(context.Request.RawUrl, context.User as ClaimsPrincipal);

                    // generate cookie
                    SessionSecurityToken sst = new SessionSecurityToken(transformedPrincipal, TimeSpan.FromHours(8));
                    sst.IsReferenceMode = true; // used when there are a lot of claims - will be faster
                    sst.IsPersistent = true;  

                    // write cookie to session
                    FederatedAuthentication.SessionAuthenticationModule.WriteSessionTokenToCookie(sst);

                    // write to context
                    context.User = transformedPrincipal;
                    Thread.CurrentPrincipal = transformedPrincipal;
                }

            }
        }
    }


}

Custom ClaimsAuthenticationManager:

public class RomesClaimsAuthenticationManager : ClaimsAuthenticationManager
{

    // PROBLEM - THIS GETS HIT 27+ times on original page load
    public override ClaimsPrincipal Authenticate(string resourceName, ClaimsPrincipal incomingPrincipal)
    {
        if (incomingPrincipal != null && incomingPrincipal.Identity.IsAuthenticated == true)
        {
            ((ClaimsIdentity)incomingPrincipal.Identity).AddClaim(new Claim(ClaimTypes.Email, "[email protected]"));
            // i will be making a db call here to get add'l user info from DB, and then convert it into claims
        }
        return incomingPrincipal;
    }
}

Web.config file:

<configSections>    
  <!--this is required for custom ClaimsAuthorizationManager-->
  <section name="system.identityModel" type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
  <!--this will allow us to write ClaimsPrincipal to a cookie, saving from calls to db on each request-->
  <section name="system.identityModel.services" type="System.IdentityModel.Services.Configuration.SystemIdentityModelServicesSection, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
</configSections>
...
<system.identityModel>
  <identityConfiguration>
    <claimsAuthenticationManager type="ROMES.Admin.Security.RomesClaimsAuthenticationManager, ROMES.Admin" />
  </identityConfiguration>
</system.identityModel>

<system.identityModel.services>    
  <federationConfiguration>
    <cookieHandler  mode="Default"  requireSsl="true" />
  </federationConfiguration>
</system.identityModel.services>
<system.web>
  <authentication mode="Windows"/>
  ...
  <authorization>
    <allow roles="WINDOWS\ROMES_Admins"/>
    <deny users="*" />
  </authorization>

  <!--must be in both here and system.webServer/modules-->
  <!--see here WHY: https://msdn.microsoft.com/en-us/library/gg638734.aspx-->
  <httpModules>
    <add name="SessionAuthenticationModule" type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />     
  </httpModules>

</system.web>
<system.webServer>

  <!-- same as in system.web/httpModules-->
  <modules>
    <add name="RomesClaimsAuthorizationModule" type="ROMES.Admin.Security.RomesClaimsAuthorizationModule"/>
    <!--this module will handle reading and writing cookie for identity/claims - so that there will be no need to call db every request for user info-->
    <add name="SessionAuthenticationModule" type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />

  </modules>
  ...
</system.webServer>

My main issues are: (1) why is my Authenticate block gets called so many times and (2) Why is Session Security Token cookie does not persist - seems like such a waste of resources.

1
27 times because you have 26 resources on the page? The post auth is called with every request, if your page references some static resources and these go through the asp.net pipeline, you get your additional requests.Wiktor Zychla
thanks, @WiktorZychla. I wonder if there's a way to have all the requests after the initial one look at the cookie or the Identity in the Thread to check if user data has been loaded.ukie
I am having the same issue/concern, that until the first request is made multiple calls are madeDavid

1 Answers

-1
votes

In here

FederatedAuthentication.SessionAuthenticationModule.ContainsSessionTokenCookie(HttpContext.Current.Request.Cookies)

change to

FederatedAuthentication.SessionAuthenticationModule.ContainsSessionTokenCookie(context.Request.Cookie)

and see if the condition started to be true and your code returns.

EDITED after David's comment: context replaced with context.Request.Cookie