Export (0) Print
Expand All

Server-to-Server Authentication using Certificates

banner art

[Applies to: Microsoft Dynamics CRM 4.0]

The server-to-server authentication process is used when a non-Microsoft Dynamics CRM Web service, Windows service, or ASPX page needs to authenticate with Microsoft Dynamics CRM Online and access the Web services.

Important    Use of certificates for Microsoft Dynamics CRM Online authentication is being phased out in the very near future in favor of a lightweight user name and password authentication method described in  the Server-to-Server Authentication topic. All new code that authenticates with CRM Online should not use certificates for authentication. The Windows Live ID team will no longer be issuing new certificates.

The server-to-server authentication process with Microsoft Dynamics CRM Online is shown in the following figure.

Windows Live authentication

Note    In order to use this new authentication model you must first obtain a certificate and then follow the documented procedure to download the Sign-in Assistant software from the Windows Live Web site and associate the certificate to your Windows Live ID. The documentation is located at SDK\Walkthroughs\Authentication\CS\ServerToServer\Cert to WLID Association.doc.

The Microsoft Dynamics CRM Online authentication process involves the following steps:

  • (1,2) Retrieve a policy from the CrmDiscoveryService Web service.
  • (3,4) Retrieve a Windows Live ID (WLID) ticket from the Windows Live service.
  • (5,6) Retrieve detailed information about the specified organization and a (Crm) ticket from the CrmDiscoveryService Web service. The ticket applies to a single organization. The ticket contains an organization specific CrmService URL. Refer to the Active Directory authentication sample for additional sample code showing how to obtain organization information by using RetrieveOrganizationsRequest.
  • (7) Create an instance of the CrmAuthenticationToken class that has the CrmTicket and OrganizationName properties set to the correct values.
  • (7) Create an instance of the CrmService class that has the Url property value and the CrmAuthenticationTokenValue property value set.
  • (7) Invoke CrmService Web service methods.

In order to accomplish steps 3 and 4 shown previously, you need to purchase a certificate from a certificate provider and set up a service account in Microsoft Dynamics CRM Online. Supported certificates are listed in the following table. The Microsoft Dynamics CRM team is investigating ways to offer more choices in certificate providers.

Supported Certificates
Entrust CA (2048)

Thumbprint: 80 1d 62 d0 7b 44 9d 5c 5c 03 5c 98 ea 61 fa 44 3c 2a 58 fe  / gB1i0HtEnVxcA1yY6mH6RDwqWP4

GoDaddy Root

Thumbprint:  27 96 ba e6 3f 18 01 e2 77 26 1b a0 d7 77 70 02 8f 20 ee e4  / J5a65j8YAeJ3Jhug13dwAo8g7uQ

NetworkSolution Root

Thumbprint:  04 83 ed 33 99 ac 36 08 05 87 22 ed bc 5e 46 00 e3 be f9 d7 / BIPtM5msNggFhyLtvF5GAOO++dc


The service account represents a virtual Microsoft Dynamics CRM Online user that is the owner of any data changes made to the Microsoft Dynamics CRM Online database through Microsoft Dynamics CRM SDK Web service calls. As with any other Microsoft Dynamics CRM user account, the service account must be added to each desired organization where business data is to be modified.

Example

The following code sample shows you how to authenticate with Microsoft Dynamics CRM Online and call a CrmService method.

[C#]
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Web.Services.Protocols;
using System.Security.Cryptography.X509Certificates;

namespace Microsoft.Crm.Sdk.Walkthrough.Authentication.PartnerAuthConsoleApp
{
    // Import the Microsoft Dynamics CRM namespaces.
    using CrmSdk;
    using CrmSdk.Discovery;
 
    class PartnerAuthentication
    {
        // Certificate name installed on ISV's server.
        // The ISV is responsible for obtaining their own certificate.
        static private string _certSubjectCN = "CertName";
        // Set the name, and if necessary, the TCP port, of the server hosting Microsoft Dynamics CRM Online.
        static private string _hostname = "dev.crm.dynamics.com";
        // This is the Windows Live ID account for the ISV's service.
        static private string _serviceAcctWLID = "ISVServiceAccount@hotmail.com";
        // This is open information about Microsoft Dynamics CRM Online.
        static private string _passportDomain = "crm.dynamics.com";
        // Set the friendly name of the target organization.
        static private string _orgFriendlyName = "AdventureWorksCycle";
        // Set the type of environment, that is, INT, PPE, or PROD.
        static private WindowsLiveIdTicketAcquirer.Environment _environment =
            WindowsLiveIdTicketAcquirer.Environment.PROD;

        // Defines the expired authentication ticket error code.
        static private string EXPIRED_AUTH_TICKET = "8004A101";

        // Attempt a service call a maximum number of times before failing.
        static private int MAX_RETRIES = 5;

        public static void InvokeServiceMethod(int retryCount)
        {
            try
            {
                if (retryCount == MAX_RETRIES)
                {
                    // Display an error when the maximum retry count has been reached.  
                    throw new Exception("An error occurred while attempting to authenticate.");
                }
                else
                {
                    // STEP 1,2: Retrieve a policy from the Discovery Web service.
                    CrmDiscoveryService discoveryService = new CrmDiscoveryService();
                    discoveryService.Url = 
                        String.Format("https://{0}/MSCRMServices/2007/{1}/CrmDiscoveryService.asmx",
                        _hostname, "Passport");

                    RetrievePolicyRequest policyRequest = new RetrievePolicyRequest();
                    RetrievePolicyResponse policyResponse =
                       (RetrievePolicyResponse)discoveryService.Execute(policyRequest);

                    // STEP 3,4: Retrieve a ticket from the Windows Live service.
                    string wlidTicket = null;
                    wlidTicket = WindowsLiveIdTicketAcquirer.RetrieveTicket(RetrieveCertificate(
                        _certSubjectCN), _serviceAcctWLID, _environment, _passportDomain,
                        policyResponse.Policy);

                    // STEP 5,6: Retrieve a target organization and a ticket from the Discovery 
                    // Web service. Retrieve a list of organizations that the logged on user is
                    // a member of.
                    RetrieveOrganizationsRequest orgRequest = new RetrieveOrganizationsRequest();
                    orgRequest.PassportTicket = wlidTicket;
                    RetrieveOrganizationsResponse orgResponse =
                        (RetrieveOrganizationsResponse)discoveryService.Execute(orgRequest);
                    // Locate the target organization name using the organization friendly name.
                    String orgUniqueName = String.Empty;
                    OrganizationDetail orgInfo = null;
                    foreach (OrganizationDetail orgDetail in orgResponse.OrganizationDetails)
                    {
                        if (orgDetail.FriendlyName.Equals(_orgFriendlyName))
                        {
                            orgInfo = orgDetail;
                            orgUniqueName = orgInfo.OrganizationName;
                            break;
                        }
                    }
                    // Retrieve the CrmTicket.
                    RetrieveCrmTicketRequest crmTicketRequest = new RetrieveCrmTicketRequest();
                    crmTicketRequest.OrganizationName = orgUniqueName;
                    crmTicketRequest.PassportTicket = wlidTicket;
                    RetrieveCrmTicketResponse crmTicketResponse =
                       (RetrieveCrmTicketResponse)discoveryService.Execute(crmTicketRequest);

                    // STEP 7: Create and configure an instance of the CrmService Web service.
                    CrmAuthenticationToken token = new CrmAuthenticationToken();
                    token.AuthenticationType = 1;
                    token.CrmTicket = crmTicketResponse.CrmTicket;
                    token.OrganizationName = crmTicketResponse.OrganizationDetail.OrganizationName;

                    CrmService crmService = new CrmService();
                    crmService.Url = crmTicketResponse.OrganizationDetail.CrmServiceUrl;
                    crmService.CrmAuthenticationTokenValue = token;

                    // Invoke the desired CrmService Web service methods.
                    WhoAmIRequest whoRequest = new WhoAmIRequest();
                    WhoAmIResponse whoResponse = (WhoAmIResponse)crmService.Execute(whoRequest);

                    systemuser user = (systemuser)crmService.Retrieve(
                        EntityName.systemuser.ToString(), whoResponse.UserId, new AllColumns());

                    Console.WriteLine("Login ISV user's name is {0}", user.fullname);
                }
            }
            catch (SoapException ex)
            {
                // Handle the exception thrown from an expired ticket condition.
                if (GetErrorCode(ex.Detail) == EXPIRED_AUTH_TICKET)
                {
                    // This will retry the CrmService Web service call.
                    InvokeServiceMethod(retryCount++);
                }

                // If this was some other SOAP exception, rethrow this exception.
                throw ex;
            }
            catch (Exception ex)
            {
                // Handle the MAX_RETRY exception here.
                // This sample will rethrow the exception.
                throw ex;
            }
        }

        private static string GetErrorCode(XmlNode errorInfo)
        {
            XmlNode code = errorInfo.SelectSingleNode("//code");

            if (code != null)
                return code.InnerText;
            else
                return "";
        }

        protected static X509Certificate2 RetrieveCertificate(string subjectCN)
        {
            X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
            store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);

            // Microsoft recommends keeping the third parameter of the Find method 
            // set to true for retrieving valid certs.
            X509Certificate2Collection certs = 
                store.Certificates.Find(X509FindType.FindBySubjectName, subjectCN, true);

            if (null != store)
            {
                store.Close();
            }

            if (null == certs || certs.Count < 1)
            {
                throw new Exception("Cannot find specified certificate.");
            }

            ValidateCert(certs[0]);

            // Return the first match.
            return certs[0];
        }

        static void ValidateCert(X509Certificate2 cert)
        {
            X509Chain chain = new X509Chain();

            // Check entire chain for revocation.
            chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain;

            // Check online or offline revocation lists.
            chain.ChainPolicy.RevocationMode = X509RevocationMode.Online;

            // Set a time out for online revocation list.
            chain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan(0, 0, 30);

            // No exceptions, check all properties for checking all flags.
            chain.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag;

            // Set or modify time of verification.
            //chain.ChainPolicy.VerificationTime = new DateTime(1999, 1, 1);

            chain.Build(cert);

            if (chain.ChainStatus.Length != 0)
            {
                // Throw exception here for invalid certificate.  
            }
        }
    }
}

The CrmDiscoveryService Web service is accessed through the global URL of the Microsoft Dynamics CRM Online server:

https://dev.crm.dynamics.com/MSCRMServices/2007/Passport/CrmDiscoveryService.asmx

If the (Crm) ticket expires during application execution, a new ticket must be obtained and assigned to the CrmTicket property of the CrmAuthenticationToken instance. If you try to access the CrmService Web methods with an expired ticket, a SOAP exception is thrown. The SoapException.Detail.Innertext property contains the error code value of "8004A101".

Note that, in real-world scenarios, you would never authenticate and then immediately check for an expired ticket as this sample shows. Instead, you would authenticate and make additional Web service method calls. Part of your software design would be to catch SOAP exceptions from Microsoft Dynamics CRM Web service calls and check for an expired authentication ticket.

To access the Windows Live authentication service over the Internet and obtain a Windows Live ID ticket, the code sample uses the WindowsLiveIdTicketAcquirer class that is provided as source code in the Server\Helpers\CS\CrmOnlineAuth folder of the SDK download.

The complete code sample can be found in the SDK\Walkthroughs\Authentication\CS|VB\ServerToServer folder of the SDK download.

See Also

Concepts

Reference

Other Resources


© 2010 Microsoft Corporation. All rights reserved.


Show:
© 2014 Microsoft