In an earlier article called Integrating Microsoft Dynamics CRM via App Fabric, we saw how we can integrate Microsoft Dynamics CRM 2011 with an external application via the Windows Azure platform App Fabric. We converted a classical pull model of integration to a more efficient push model. The data was queried only when needed.
I would like to draw your attention to the authentication mechanism that was employed to authenticate with the Access Control Service (ACS). We used the management key approach and this implicitly made assumption that the key can be shared between CRM system and the owner of the syncing module. In many of the integration scenarios the CRM system and the external application (including the syncing module) may not be owned by the same business. The sharing of the ACS account’s management key is not possible.
Editor’s Note: For more overview material on AppFabric’s ACS there is an excellent article here: Access Control in the Cloud: Windows Azure AppFabric’s ACS.
Consider the scenario where an ISV has setup an endpoint on the App Fabric to which they want other CRM systems, its customers, to post data so that it can provide appropriate service. The ISV owns the Azure account and the rules on the ACS. It needs to enable its customer’s CRM systems to be able to post data to its endpoint and also be able to easily control and filter who is allowed to do so.
ACS provides the use of SAML tokens to authenticate and this is a good alternative for our need. Our goal is to allow CRM system to authenticate via SAML token and also allow ISV to configure rules in ACS based on the token’s issuer signature.
We start by first procuring an X509 certificate. You can use a self signed certificate too. Generate both private (.pfx) and public (.cer) certificate parts of the certificate. Add the .pfx certificate to the certificate store of the Async box, as we are running the plug-in asynchronously, under Computer account -> Local computer -> Personal -> Certificates.
Update the RetrieveAuthBehavior() show in the earlier blog sample with the code below.
!-->private TransportClientEndpointBehavior RetrieveAuthBehavior() { // Behavior TransportClientEndpointBehavior behavior = new TransportClientEndpointBehavior(); behavior.CredentialType = TransportClientCredentialType.Saml; behavior.Credentials.Saml.SamlToken = GetTokenString(); return behavior; } private string GetTokenString() { // Generate Saml assertions.. string issuerName = "localhost"; Saml2NameIdentifier saml2NameIdentifier = new Saml2NameIdentifier(issuerName); // this is the issuer name. Saml2Assertion saml2Assertion = new Saml2Assertion(saml2NameIdentifier); Uri acsScope = ServiceBusEnvironment.CreateAccessControlUri(SolutionName); saml2Assertion.Conditions = new Saml2Conditions(); saml2Assertion.Conditions.AudienceRestrictions.Add( new Saml2AudienceRestriction(acsScope)); // this is the ACS uri. saml2Assertion.Conditions.NotOnOrAfter = DateTime.UtcNow.AddHours(1); saml2Assertion.Conditions.NotBefore = DateTime.UtcNow.AddHours(-1); X509Certificate2 localCert = RetrieveCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindBySubjectName, issuerName); if (!localCert.HasPrivateKey) { throw new InvalidPluginExecutionException("Certificate should have private key."); } saml2Assertion.SigningCredentials = new X509SigningCredentials(localCert); // Add organization assertion. saml2Assertion.Statements.Add( new Saml2AttributeStatement( new Saml2Attribute("http://schemas.microsoft.com/crm/2007/Claims", "Org1"))); // The submitter should always be a bearer. saml2Assertion.Subject = new Saml2Subject(new Saml2SubjectConfirmation(Saml2Constants.ConfirmationMethods.Bearer)); // Wrap it into a security token. Saml2SecurityTokenHandler tokenHandler = new Saml2SecurityTokenHandler(); Saml2SecurityToken securityToken = new Saml2SecurityToken(saml2Assertion); // Serialize the security token. StringBuilder sb = new StringBuilder(); using (XmlWriter writer = XmlTextWriter.Create(new StringWriter(sb, CultureInfo.InvariantCulture))) { tokenHandler.WriteToken(writer, securityToken); writer.Close(); } return sb.ToString(); } !-- code>
The code generates a self-signed token to authenticate to the app fabric. We could add any claims to the generated token, for example, adding the organization claim would be quite useful.
Configuring new rules in the App fabric
Since we are trying to authenticate via the signed SAML token, we would need to add the issuer and rules into the ACS to identify the token and issue “Send” claim in response. I am using the acm.exe that ships with the App Fabric sdk to configure rules in ACS.
First create the issuer entry in ACS-SB.
>Acm.exe create issuer -name:LocalhostIssuer -issuername:localhost -certfile:localhost.cer -algorithm:X509
Object created successfully (ID:'iss_334389048b872a533002b34d73f8c29fd09efc50')
Next retrieve the base scope of the service bus: >Acm.exe getall scope
Use the scope id and issuer id to create the following rule.
>Acm.exe create rule -scopeid:scp_b693af91ede5d4767c56ef3df8de8548784e51cb -inclaimissuerid:iss_334389048b872a533002b34d73f8c29fd09efc50 -inclaimtype:Issuer -inclaimvalue:localhost -outclaimtype:net.windows.servicebus.action -outclaimvalue:Send -name:RuleSend
The rule basically says to output “Send” claim when the input claim (any claim) is signed by the specific issuer
With the above rule in place any organization in the customers CRM system will be allowed to post. If the CRM system is multi tenant, i.e. with multiple organizations, we can make the rule more granular to allow only certain organizations.
Rule using organization claim.
>Acm.exe create rule -scopeid:scp_b693af91ede5d4767c56ef3df8de8548784e51cb -inclaimissuerid:iss_334389048b872a533002b34d73f8c29fd09efc50 -inclaimtype: http://schemas.microsoft.com/crm/2007/Claims -inclaimvalue:Org1 -outclaimtype:net.windows.servicebus.action -outclaimvalue:Send -name:RuleOrgSend
Cheers,
Image may be NSFW.Clik here to view.