Search code examples
asp.net-web-apiowinadfsws-federationowin-middleware

OWIN - clear invalid WSFederation cookies


I implemented an ASP.Net Web API 2 project with ADFS cookie authentication and hosted it on IIS. All works fine.

However, some clients have got old cookies which became invalid because of configuration changes. Such cookies cause following error when calling my API:

[CryptographicException: Key not valid for use in specified state.
]
   System.Security.Cryptography.ProtectedData.Unprotect(Byte[] encryptedData, Byte[] optionalEntropy, DataProtectionScope scope) +447
   System.IdentityModel.ProtectedDataCookieTransform.Decode(Byte[] encoded) +49

[InvalidOperationException: ID1073: A CryptographicException occurred when attempting to decrypt the cookie using the ProtectedData API (see inner exception for details). If you are using IIS 7.5, this could be due to the loadUserProfile setting on the Application Pool being set to false. ]
   System.IdentityModel.ProtectedDataCookieTransform.Decode(Byte[] encoded) +329
   System.IdentityModel.Tokens.SessionSecurityTokenHandler.ApplyTransforms(Byte[] cookie, Boolean outbound) +167
   System.IdentityModel.Tokens.SessionSecurityTokenHandler.ReadToken(XmlReader reader, SecurityTokenResolver tokenResolver) +826
   System.IdentityModel.Tokens.SessionSecurityTokenHandler.ReadToken(Byte[] token, SecurityTokenResolver tokenResolver) +92
   System.IdentityModel.Services.SessionAuthenticationModule.ReadSessionTokenFromCookie(Byte[] sessionCookie) +569
   System.IdentityModel.Services.SessionAuthenticationModule.TryReadSessionTokenFromCookie(SessionSecurityToken& sessionToken) +306
   System.IdentityModel.Services.SessionAuthenticationModule.OnAuthenticateRequest(Object sender, EventArgs eventArgs) +159
   System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +142
   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +92

The obvious workaround is to clear the cookies. However, it's likely that I'll change the cookies configuration again in future, so I'd like to clear all invalid cookies automatically from the API.

I've tried adding a custom OWIN middleware and overriding IExceptionHandler.

Here's my WIF config:

<system.identityModel>
  <identityConfiguration>
    <audienceUris>
      <add value="https://my.web-api.com" />
    </audienceUris>
    <issuerNameRegistry type="System.IdentityModel.Tokens.ValidatingIssuerNameRegistry, System.IdentityModel.Tokens.ValidatingIssuerNameRegistry">
      <authority name="ADFS">
        <keys>
          <add thumbprint="--a thumbprint--" />
        </keys>
        <validIssuers>
          <add name="http://my.adfs.com/adfs/services/trust" />
        </validIssuers>
      </authority>
    </issuerNameRegistry>
  </identityConfiguration>
</system.identityModel>
<system.identityModel.services>
  <federationConfiguration>
    <wsFederation issuer="https://my.adfs.com/adfs/ls" realm="https://my.web-api.com" requireHttps="true" passiveRedirectEnabled="false"
                  persistentCookiesOnPassiveRedirects="true" />
    <cookieHandler name="my.cookie" path="/" persistentSessionLifetime="7.0:0:0" />
    <serviceCertificate>
      <certificateReference x509FindType="FindBySubjectName" findValue="my.web-api.com" storeLocation="LocalMachine" storeName="My" />
    </serviceCertificate>
  </federationConfiguration>
</system.identityModel.services>

Here's my Startup class:

public class Startup
{
    public void Configuration(IAppBuilder appBuilder)
    {
        var config = new HttpConfiguration();

        config.Services.Replace(typeof(IExceptionHandler), new CryptographicExceptionHandler());
        WebApiConfig.Register(config);
        appBuilder.UseWebApi(config);
        appBuilder.Use<ClearInvalidCookiesMiddleware>();
    }
}

No matter what's inside CryptographicExceptionHandler and ClearInvalidCookiesMiddleware, their code is not called and I'm getting 500 error. I also tried to move ClearInvalidCookiesMiddleware before UseWebApi.

My aim is to add Set-Cookie response header to clear invalid cookies and return 401 or a redirect.

How can I make OWIN to customize the response in this case?


Solution

  • The solution appeared to override SessionAuthenticationModule.OnAuthenticateRequest and call SignOut() in case of exceptions:

    class ClearInvalidCookiesSessionAuthenticationModule : SessionAuthenticationModule
    {
        protected override void OnAuthenticateRequest(object sender, EventArgs eventArgs)
        {
            try
            {
                base.OnAuthenticateRequest(sender, eventArgs);
            }
            catch(InvalidOperationException ex) when (ex.InnerException is CryptographicException) // Invalid cookie signing key
            {
                SignOut();
            }
            catch(System.Xml.XmlException) // Invalid cookie structure
            {
                SignOut();
            }
        }
    }
    

    To use the inherited class instead of default one, one should insert following line inside Web.config:

    <system.webServer>
      <modules ...>
        <!-- Insert the line below or replace existing SessionAuthenticationModule -->
        <add name="SessionAuthenticationModule" preCondition="managedHandler"
             type="MyNamespace.ClearInvalidCookiesSessionAuthenticationModule, MyAssembly" />
        ...
      </modules>
    ...
    </system.webServer>