wcf
wcf copied to clipboard
InternalTokenReference mismatch when using SecurityTokenProvider GetToken vs WSTrustChannelFactory Issue
GetToken SAML token using SecurityTokenProvider GetToken returns a GenericXmlSecurityToken with different type of Internal/External Identifiers then WSTrustChannelFactory Issue did in the full framework.
Using .NET 6 - Issued with SecurityTokenProvider System.IdentityModel.Tokens.GenericXmlSecurityKeyIdentifierClause is returned.
-
**InternalTokenReference** {System.IdentityModel.Tokens.GenericXmlSecurityKeyIdentifierClause} System.IdentityModel.Tokens.SecurityKeyIdentifierClause {System.IdentityModel.Tokens.GenericXmlSecurityKeyIdentifierClause} CanCreateKey false bool ClauseType null string DerivationLength 0 int Id null string
Using .NET Framework - Issued with WSTrustChannelFactory
-
InternalTokenReference {SamlAssertionKeyIdentifierClause(AssertionId = '_cbbf45eb-d99b-4ee0-bf9b-9ab6dc3d7d91')} System.IdentityModel.Tokens.SecurityKeyIdentifierClause {System.IdentityModel.Tokens.SamlAssertionKeyIdentifierClause} AssertionId "_cbbf45eb-d99b-4ee0-bf9b-9ab6dc3d7d91" string CanCreateKey false bool ClauseType null string DerivationLength 32 int Id null string
To Reproduce
binding.Security.Mode = SecurityMode.TransportWithMessageCredential;
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
binding.Security.Message.EstablishSecurityContext = false;
// Credentials - UserPass
var clientCredentials = new ClientCredentials();
clientCredentials.UserName.UserName = "Username";
clientCredentials.UserName.Password = "Pass";
var trustCredentials = new WSTrustChannelClientCredentials(clientCredentials);
var securityKeyType = SecurityKeyType.SymmetricKey;
var issuerAddress = new EndpointAddress(new Uri("https://sts.example.com/adfs/services/trust/13/usernamemixed"));
var tokenParams = WSTrustTokenParameters.CreateWS2007FederationTokenParameters(binding, issuerAddress);
tokenParams.KeyType = securityKeyType;
var tokenRequirement = new SecurityTokenRequirement();
tokenRequirement.Properties[IssuedSecurityTokenParametersProperty] = tokenParams;
tokenRequirement.Properties[TargetAddressProperty] = new EndpointAddress(appliesTo); //Realm
tokenRequirement.Properties[SecurityAlgorithmSuiteProperty] = SecurityAlgorithmSuite.Basic256;
var tokenManager = trustCredentials.CreateSecurityTokenManager();
var tokenProvider = tokenManager.CreateSecurityTokenProvider(tokenRequirement);
((ICommunicationObject)tokenProvider).Open();
var token = tokenProvider.GetToken(Timeout.InfiniteTimeSpan);
return token;
Expected behavior a GenericXmlSecurityToken with SamlAssertionKeyIdentifierClause should be expected. The AssertionId/DerivationLength field is missing from GenericXmlSecurityKeyIdentifierClause type. GenericXmlSecurityKeyIdentifierClause has Id field but it´s null.
** Additional Information ** The token works when using it to connect to ADFS to get another token, and using it for other WCF Services. But we cannot Serialize/Deserialize like before without AsserationId
** Serialize **
{
XmlRepresentation = genericToken.TokenXml,
ValidFrom = genericToken.ValidFrom,
ValidTo = genericToken.ValidTo,
ProofToken = ((BinarySecretSecurityToken) genericToken.ProofToken)?.GetKeyBytes(),
#if NETFRAMEWORK
AssertionId = ((System.IdentityModel.Tokens.SamlAssertionKeyIdentifierClause)genericToken.InternalTokenReference).AssertionId,
AssertionDerivationKeyLength = ((System.IdentityModel.Tokens.SamlAssertionKeyIdentifierClause)genericToken.InternalTokenReference).DerivationLength
#else
AssertionId = ((SamlAssertionKeyIdentifierClause)genericToken.InternalTokenReference).Id,
AssertionDerivationKeyLength = ((SamlAssertionKeyIdentifierClause)genericToken.InternalTokenReference).DerivationLength
#endif
};
}
The above serialize example throws an exception: System.InvalidCastException: Unable to cast object of type 'System.IdentityModel.Tokens.GenericXmlSecurityKeyIdentifierClause' to type 'System.IdentityModel.Tokens.SamlAssertionKeyIdentifierClause'.
Deserialize
var assertionKeyIdentifier = new SamlAssertionKeyIdentifierClause(tokenSerialized.AssertionId);
var genericToken = new GenericXmlSecurityToken(
tokenSerialized.XmlRepresentation,
tokenSerialized.ProofToken != null ? new BinarySecretSecurityToken(tokenSerialized.ProofToken) : null,
tokenSerialized.ValidFrom,
tokenSerialized.ValidTo,
assertionKeyIdentifier,
assertionKeyIdentifier,
null);
Can you check GenericXmlSecurityToken.Id? I believe it should have the same Id as AssertionId. The code here looks like it should pick up the AssertionId from the token and on .NET Framework SamlSecurityToken.Id just returns the SamlAssertion.AssertionId.
If that doesn't work, I think you can get the raw XML out but getting GenericXmlSecurityToken.TokenXml. You can then either use something like XPath or Linq2XML or something similar to extract the assertion Id, or you could reference the Microsoft.IdentityModel.Protocols.WsTrust package which we use and use one of the serializers there to deserialize the token into their object model. Code for that would look something like this:
var samlTokenHandler = new Microsoft.IdentityModel.Tokens.Saml.SamlSecurityTokenHandler();
Microsoft.IdentityModel.Tokens.Saml.SamlSecurityToken samlSecurityToken = samlTokenHandler.ReadSamlToken(GenericXmlSecurityToken.TokenXml.OuterXml);
var samlAssertion = samlSecurityToken.Assertion;
var assertionId = samlAssertion.AssertionId;
Close this since no response. Feel free to re-activate if this is still an issue.