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:
- Even on initial page load my custom RomesClaimsAuthenticationManager.Authenticate method gets called 27+ times (I assume some calls are parrallel/async).
- 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.