Question

I am looking for ways to troubleshoot SAML token parsing errors when using WIF. I am pretty new to this technology, so I may be missing some obvious techniques.

I wrote a sample MVC application that uses external authentication using WS-Federation. I tried it out with ThinkTecture server locally and it works like a charm. However when I tried the same approach with external STS Server (CA CloudMinder), I am getting following errror in WIF:

Application Error: 'Element' is an invalid XmlNodeType. System.Xml.XmlException XmlException System.Xml.XmlException: 'Element' is an invalid XmlNodeType.
   at System.Xml.XmlReader.ReadEndElement()
   at System.IdentityModel.Tokens.Saml2SecurityTokenHandler.ReadAssertion(XmlReader reader)
   at System.IdentityModel.Tokens.Saml2SecurityTokenHandler.ReadToken(XmlReader reader)
   at System.IdentityModel.Tokens.SecurityTokenHandlerCollection.ReadToken(XmlReader reader)
   at System.IdentityModel.Services.TokenReceiver.ReadToken(String tokenXml, XmlDictionaryReaderQuotas readerQuotas, FederationConfiguration federationConfiguration)
   at System.IdentityModel.Services.WSFederationAuthenticationModule.SignInWithResponseMessage(HttpRequestBase request)
   at System.IdentityModel.Services.WSFederationAuthenticationModule.OnAuthenticateRequest(Object sender, EventArgs args)
   at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
   at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) Void ReadEndElement()    at System.Xml.XmlReader.ReadEndElement()
   at System.IdentityModel.Tokens.Saml2SecurityTokenHandler.ReadAssertion(XmlReader reader)
   at System.IdentityModel.Tokens.Saml2SecurityTokenHandler.ReadToken(XmlReader reader)
   at System.IdentityModel.Tokens.SecurityTokenHandlerCollection.ReadToken(XmlReader reader)
   at System.IdentityModel.Services.TokenReceiver.ReadToken(String tokenXml, XmlDictionaryReaderQuotas readerQuotas, FederationConfiguration federationConfiguration)
   at System.IdentityModel.Services.WSFederationAuthenticationModule.SignInWithResponseMessage(HttpRequestBase request)
   at System.IdentityModel.Services.WSFederationAuthenticationModule.OnAuthenticateRequest(Object sender, EventArgs args)
   at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
   at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

This is obviously, related to format of actual SAML token that is being sent back. I figured that I can capture the token itself as follows and log it:

void WSFederationAuthenticationModule_SignInError(object sender, ErrorEventArgs e)
{                           
  var message = FederatedAuthentication.WSFederationAuthenticationModule.GetSignInResponseMessage(
     new HttpRequestWrapper(HttpContext.Current.Request));
}

I cannot share the actual token itself. But I am wondering, if you know of any ways how to troubleshoot those errors - for example some way of manually validating the token.

Any suggestions appreciated.

Was it helpful?

Solution

Ok, so the one technique that I used that proved very useful to narrow-down on the issue was to subclass and register my own Saml2SecurityTokenHandler.

This helped me to verify that the problem was in the order of elements within SAML token - apparently WIF is inot very forgiving when it comes to location of Signature element within SAML assertion - it requires it to be immediately after Issuer element.

public class ForgivingSaml2SecurityTokenHandler : Saml2SecurityTokenHandler
{
    public override SecurityToken ReadToken(XmlReader reader)
    {
        reader = SanitizeToken(reader);
        return base.ReadToken(reader);
    }

    XmlReader SanitizeToken(XmlReader reader)
    {             
        var xmlDoc = new XmlDocument();
        xmlDoc.Load(reader);
        var ns = new XmlNamespaceManager(xmlDoc.NameTable);
        ns.AddNamespace("ds", "http://www.w3.org/2000/09/xmldsig#");
        ns.AddNamespace("sa", "urn:oasis:names:tc:SAML:2.0:assertion");
        var issuerNode = xmlDoc.SelectSingleNode("//sa:Issuer", ns);
        var signatureNode = xmlDoc.SelectSingleNode("//ds:Signature", ns);
        signatureNode.ParentNode.InsertAfter(signatureNode, issuerNode);
        string sanitizedToken = xmlDoc.InnerXml;            
        return new XmlTextReader(new StringReader(sanitizedToken));           
    }    
}

So the above would not really resolve the issue, as modifying the token caused the signature check to fail, but it allowed me to verify that the order was indeed the issue, as I was able to modify the correct token and make it fail the same way as problematic token was behaving - apparently the token structure checks are being performed before signature checks by WIF.

To register my subclassed token handler i used following registration in web.config file:

<identityConfiguration saveBootstrapContext="true">
  <securityTokenHandlers>
    <remove type="System.IdentityModel.Tokens.Saml2SecurityTokenHandler, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
    <add type="ExploringSaml.ForgivingSaml2SecurityTokenHandler, ExploringSaml" ></add>        
  </securityTokenHandlers>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top