MachineKeySessionSecurityTokenHandler and the session token expiring between application restarts - wif

In my MVC application, I am using forms authentication to authenticate the user and then System.IdentityModel.Services.SessionAuthenticationModule to persist the session.
While I'm not yet at the point where it's necessary, I did want to utilize System.IdentityModel.Services.Tokens.MachineKeySessionSecurityTokenHandler so that the application will live nicely on a web farm (as described by Dominick Baier here).
The problem I have is that, given the machineKey-based handling, I would expect that not only would the session be valid from server machine to machine, but should also survive application restarts. However, any time I either restart or rebuild the application, upon hitting the application in the browser, the cookie apparently becomes invalid and I get bounced to the authentication screen. Once authenticated again, everything is fine and the session remains. However, the next time the app restarts or is rebuilt, I'm forced to re-authenticate.
I'm sure this is an aspect of WIF that I'm not getting, but I just don't know where to turn from here. I'm not afraid of having to extend MachineKeySessionSecurityTokenHandler, but I'd like to make sure that I understand what's going on here before I proceed. I understand that the default SessionSecurityTokenHandler uses DPAPI in combination with some identifier from the app pool for its cryptography, so it makes sense that this would happen in that case, but the behavior in MachineKeySessionSecurityTokenHandler puzzles me. Is there still some identifier in the application that gets recreated on restart on which MachineKeySessionSecurityTokenHandler depends? Am I just missing a setting?
Here are the pertinent parts from my web.config:
<configSections>
<section name="system.identityModel"
type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
</configSections>
...
<system.identityModel>
<identityConfiguration>
<securityTokenHandlers>
<remove type="System.IdentityModel.Tokens.SessionSecurityTokenHandler, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<add type="System.IdentityModel.Services.Tokens.MachineKeySessionSecurityTokenHandler, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
</securityTokenHandlers>
</identityConfiguration>
</system.identityModel>
...
<system.web>
<machineKey compatibilityMode="Framework45"
validationKey="E27893..."
decryptionKey="ABC..."
validation="SHA1" decryption="AES" />
<authentication mode="Forms">
<forms loginUrl="~/Account/Login"
timeout="10080" />
</authentication>
</system.web>
...
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="SessionAuthenticationModule"
type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
</modules>
</system.webServer>

hm - if you are setting the machine key explicitly (like you seem to do) - I don't see a reason why this would not work. Maybe you are using other cookies, sessions etc that trigger the re-auth problem?

OK, this was my stupid fault. For reasons that are not pertinent here, I set the FedAuth cookie name to something non-standard (i.e. not "FedAuth"), like this:
FederatedAuthentication
.FederationConfiguration
.CookieHandler
.Name = "SomeThingNotStandard";
The problem was I was setting it like this only at the moment that I issued the token on a successful login. Well, of course everything's gonna be fine because now the in-memory configuration is looking for "SomeThingNotStandard" as the cookie name. But, on an app restart, the configuration will be back to the default, looking for "FedAuth", not "SomeThingNotStandard". This forces the re-login, which upon success, re-configures the app and then everything seems fine.
So I put the code bit above in Application_Start() and it works fine across re-builds and re-starts.
Dumb move on my part.
Edit:
I moved this to configuration
<system.identityModel.services>
<federationConfiguration>
<cookieHandler
name="SomeThingNotStandard" />
</federationConfiguration>
</system.identityModel.services>

Related

Purpose of UrlRouting.axd

What is the purpose of "UrlRouting.axd" in web.config?
To get ASP.NET routing to work in IIS integrated mode everybody suggests adding the following line, with absolutely zero explanation as to why it should make routing work:
<add name="UrlRoutingHandler" preCondition="integratedMode" verb="*" path="UrlRouting.axd" type="System.Web.HttpForbiddenHandler, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
At first blush this line makes no sense because it just maps "UrlRouting.axd" (that is never invoked from the client) to the forbidden handler (which will always return an error). I don't really like mindlessly copy-pasting things I have no understanding of, so an explanation of how it works will help me. :)

ClaimsAuthorizationManager issues

I am using a custom ClaimsAuthorizationManager to do Authorization in MVC, but I am running into an issue.
Even if I simply return 'true' in the CheckAccess method, all
files/images are being blocked with a 500 run-time error because:
Exception information:
Exception type: NotSupportedException
Exception message: ID1075: The ClaimsAuthorizationModule cannot be used without the current principal (HttpContext.Current.User) being a
ClaimsPrincipal. at
System.IdentityModel.Services.ClaimsAuthorizationModule.Authorize()
I am not changing anything regarding the Current Principal earlier in the application... Thoughts? I am stumped and searching for that error reveals nothing...
<system.webServer>
<modules>
...
<add name="ClaimsAuthorizationModule" type="System.IdentityModel.Services.ClaimsAuthorizationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
</modules>
</system.webServer>
<system.identityModel>
<identityConfiguration>
<claimsAuthorizationManager type="NamespaceFun.CustomAuthorizationManager, NamespaceFun" >
<policy resource="http://localhost:52606/" action="GET">
</policy>
</claimsAuthorizationManager>
...
</identityConfiguration>
</system.identityModel>
public class CustomAuthorizationManager : ClaimsAuthorizationManager
{
public override bool CheckAccess(AuthorizationContext context)
{
return true;
}
}
I just had the same problem, so I compared my web.config to one in the WIF book, and it turns out I was missing preCondition="managedHandler" from the end of the add element, and it seems you're missing it too.
It also seems to be missing from the MSDN example.
Correct way:
<add name="ClaimsAuthorizationModule"
type="System.IdentityModel.Services.ClaimsAuthorizationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
preCondition="managedHandler" />
It figures I would ask a question and then answer it myself an hour later. But this had me perplexed for multiple days. It wasn't until it was actually blocking me from getting other stuff done that I had to dig into it.
So anyway, what I did was pretty simple. This example of the ClaimsAuthorizationManager from Microsoft does Not have any issues serving up Images.
http://code.msdn.microsoft.com/vstudio/Claims-Aware-MVC-523e079b
So I compared the Web.config files from both applications and found out they were 'amazingly' similar.
The only difference is that my intranet application had these handlers in the system.webServer section:
<handlers>
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
I actually need to figure out what these are doing, but at the moment, removing them solves my original issue.
WIF is still a giant 'black box' in my opinion. Its hard to adopt a new technology when Googling for errors returns absolutely nothing.
I actually think it is a really bad idea to use the claims authorization module at all. The reason is that the module is based off URLs - but in MVC everything is keyed off the routing table or controllers/actions.
Whenever the two get out of sync you might have holes in your authorization policy (plus the issue with static files that you already encountered). I actually prefer the attribute based approach - I built to authorize attributes (one for MVC, one for Web API) that play nice with ASP.NET:
http://leastprivilege.com/2012/10/26/using-claims-based-authorization-in-mvc-and-web-api/
In my case, the reason why files (including JavaScript) were being blocked when using custom ClaimsAuthorizationManager was because of the runAllManagedModulesForAllRequests attribute in modules tag:
<modules runAllManagedModulesForAllRequests="true">
This results in processing all requests by the custom ClaimsAuthorizationManager even if the request was not for managed content. Removing that attribute solves the issue.

Why would FederatedAuthentication.WSFederationAuthenticationModule be null in MVC Azure ACS Federated Authentication?

I'm trying to put together FederatedAuthentication with .NET 4.5, MVC 4, and active redirect using a custom server-side login page, using code from this tutorial, and from this code sample.
Redirecting to the LogOn method of my AccountController works fine, and the method looks like this:
public ActionResult LogOn()
{
HrdClient hrdClient = new HrdClient();
WSFederationAuthenticationModule fam = FederatedAuthentication.WSFederationAuthenticationModule; /*** Fails here because this is null **/
HrdRequest request = new HrdRequest(fam.Issuer, fam.Realm, context: Request.QueryString["ReturnUrl"]);
IEnumerable<HrdIdentityProvider> hrdIdentityProviders = hrdClient.GetHrdResponse(request);
ViewData["Providers"] = hrdIdentityProviders;
return View();
}
This fails because FederatedAuthentication.WSFederationAuthenticationModule is null.
Using VS 2012, I've run the new Identity and Access wizard (which seems to replace the old STS dialog). This has given me a folder of FederationMetadata, which appears correct, and several modifications to my Web.Config. In particular, the modules section looks like this:
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<modules runAllManagedModulesForAllRequests="true">
<add name="WSFederationAuthenticationModule" type="System.IdentityModel.Services.WSFederationAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" preCondition="managedHandler" />
<add name="SessionAuthenticationModule" type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" preCondition="managedHandler" />
</modules>
And having seen SO answers 8937123 and 8926099, I've added the following as well:
<httpModules>
<add name="WSFederationAuthenticationModule" type="Microsoft.IdentityModel.Web.WSFederationAuthenticationModule, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</httpModules>
And finally my nuget packages config shows Microsoft.IdentityModel, which is correctly referenced by the MVC app:
<packages>
<package id="Microsoft.IdentityModel" version="6.1.7600.16394" targetFramework="net45" />
</packages>
I've also seen this question on social.msdn, which just seems to suggest that the STS dialog does need to be run.
Can anybody explain why FederatedAuthentication.WSFederationAuthenticationModule would be null, and what I can do to stop this happening?
I managed to fix this myself, and since there are a few unanswered questions similar to this on SO, I'll leave the question up and post my own answer.
The issue is to do with upgrading the MVC application to .NET 4.5. Much of the WIF functionality remains the same (at least on the surface), but the classes have all moved to different assemblies. I fixed my problem following the migration guidelines here: http://msdn.microsoft.com/en-us/library/jj157089.aspx
The most important thing is to remove old references to the Microsoft.IdentityModel package (v 3.5.0) and make sure they are replaced by similar references to the System.IdentityModel and System.IdentityModel.Services dlls, which should be version 4.0, and come from the GAC rather than an external package.
My steps to fix were:
Clean out all the junk I'd added to Web.Config and start again with a default MVC Config file.
Remove the Microsoft.IdentityModel package and de-reference the dll
Run the Access and Identity wizard in VS 2012
Duplicate the System.IdentityModel.Services.WSFederationAuthenticationModule reference from <system.webServer><modules> in <system.web><httpModules>
Add <authentication mode="Forms"><forms loginUrl="~/Account/LogOn" /></authentication>
Compile, test, dance little jig of delight...
And this got the original WIF3.5 / MVC3 Code Sample working under.NET 4.5

Run WIF without LoadUserProfile = True is throwing null error

I am using WIF SSO for authentication in my website. Everything works perfect in development environment. But on deployment I got issue
Message: The data protection operation was unsuccessful. This may have
been caused by not having the user profile loaded for the current
thread's user context, which may be the case when the thread is
impersonating. ExceptionStackTrace: at
System.Security.Cryptography.ProtectedData.Protect(Byte[] userData,
Byte[] optionalEntropy, DataProtectionScope scope) at
Microsoft.IdentityModel.Web.ProtectedDataCookieTransform.Encode(Byte[]
value)
Searching abt this issue leads me to this stackoverflow question
Is it possible to run WIF without LoadUserProfile = True
I added the code mentioned but now I am getting
Value cannot be null
I am getting e.ServiceConfiguration.ServiceCertificate ServiceCertificate null. My question is what kind of certificate is this and where can I define this in my config. Do I need to place the same certificate on ACS.
here is my config section
<microsoft.identityModel>
<service>
<audienceUris>
<add value="http://localhost:9494/" />
</audienceUris>
<federatedAuthentication>
<wsFederation passiveRedirectEnabled="true" issuer="https://devworks-sb.accesscontrol.appfabriclabs.com/v2/wsfederation" realm="http://localhost:9494" requireHttps="false" />
<cookieHandler requireSsl="false" />
</federatedAuthentication>
<applicationService>
<claimTypeRequired>
<!--Following are the claims offered by STS 'https://devworks-sb.accesscontrol.appfabriclabs.com/'. Add or uncomment claims that you require by your application and then update the federation metadata of this application.-->
<claimType type="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" optional="true" />
<claimType type="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" optional="true" />
<!--<claimType type="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier" optional="true" />-->
<!--<claimType type="http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider" optional="true" />-->
</claimTypeRequired>
</applicationService>
<issuerNameRegistry type="Microsoft.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<trustedIssuers>
<add thumbprint="BE9D0A516BEC2BC820C23D5C2EA79F068C094382" name="https://devworks-sb.accesscontrol.appfabriclabs.com/" />
</trustedIssuers>
</issuerNameRegistry>
</service> </microsoft.identityModel>
thanx
First thing you mentioned that the problem occurred after deployment, is that right? In your web.config have you changed the audienceUris to http://whatever_service_name.cloudapp.net?
<audienceUris>
<add value="http://localhost:9494/" /> <== This is wrong
</audienceUris>
Next your question about certificate is NULL at e.ServiceConfiguration.ServiceCertificate, please verify the following:
A. Endpoint is added in your application Service Definition:
B. Certificate thumbprint is set in Service Configuration
C. Certificate is set in web.config which is correct above
D. Finally added the following in your web.config so certificate can be search by thumbprint:
<serviceCertificate>
<certificateReference x509FindType="FindByThumbprint" findValue="CERT_THUMB" />
</serviceCertificate>
Study these two resources which will be very helpful:
http://www.jimandkatrin.com/CodeBlog/post/Troubleshooting-Azure-issues.aspx
http://blogs.msmvps.com/marcelmeijer/blog/2012/05/04/windows-azure-wif-access-control-acs/
The root cause is likely to be you’re using DPAPI (the default configuration of WIF). Please try to do a few modifications for the application to work in Windows Azure. I would like to suggest you to check http://msdn.microsoft.com/en-us/IdentityTrainingCourse_WIFonWAZLab2010 for a tutorial.
Best Regards,
Ming Xu.

ASP MVC Route issue with dot "."

I have this route:
file{FileId}/{name}
It works perfectly unless name has a dot in it.
For example, these work:
file1/blah, file90/foo -
but it doesn't:
file1/blah.doc
All I get in this case is 404 error. Seems like it looks for the actual file blah.doc instead of use routing system.
This problem happens only in production server. I've even tried
httpRuntime relaxedUrlToFileSystemMapping="true"
but it didn't help.
Everything after the '.' is the file extension. If that extension isn't mapped to ASP.NET, it won't get handed off to the ASP.NET handler. IIS looks for a static file instead. So you need to add a handler for your case (web.config), then your route will be able to catch the request.
<validation validateIntegratedModeConfiguration="false" />
<handlers>
<add name="FileHandlerDot" verb="GET" path="file*/*.*" type="System.Web.Mvc.MvcHttpHandler, System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</handlers>
</validation>

Resources