Customer Engagement & Dynamics CRM Forum

 View Only
  • 1.  Custom Web Service No Longer Connecting to Dynamics CRM

    Posted May 11, 2022 03:12 PM

    My client has a custom .NET web service that connects to Dynamics CRM online, but lately the connection to CRM isn't being made, and the web service is encountering a Null Object exception when trying to call a method on the CrmServiceClient object.  Here is the current code to establish the connection to CRM:

    public CrmServiceClient getServiceProxy()
    {
    string connectionString = GetServiceConfiguration();
    CrmServiceClient conn = new CrmServiceClient(connectionString);
    //return (IOrganizationService)conn.OrganizationWebProxyClient != null ?
    // (IOrganizationService)conn.OrganizationWebProxyClient : (IOrganizationService)conn.OrganizationServiceProxy;
    return conn;
    }

    The connection string is: 

    <add name="ACISProd" connectionString="AuthType=Office365;Username=********@*****.com; Password=********;Url=https://********.crm.dynamics.com"/>

    The CRM is on version :

       online

    Any helpful hints on how to get the connection working again would be greatly appreciated.



    ------------------------------
    Jim Field
    Manager
    CohnReznick
    Boston MA
    ------------------------------


  • 2.  RE: Custom Web Service No Longer Connecting to Dynamics CRM

    SILVER CONTRIBUTOR
    Posted May 12, 2022 03:22 AM
    Hi

    Microsoft is moving away from WS-Trust authentication protocol and retiring this method. It could be they have now switched it off for your tenant as they seem to be doing it in phases. You will need to switch to OAuth2 protocol to authenticate in future if this is the issue.

    Kind Regards

    Jon

    ------------------------------
    Jonathan Farmer
    Business Systems Manager
    SWS Broadband
    ------------------------------



  • 3.  RE: Custom Web Service No Longer Connecting to Dynamics CRM

    SILVER CONTRIBUTOR
    Posted May 12, 2022 04:21 AM

    Hi There

     

    I had to rewrite these things for us when MS withdrew the old Protocol for connecting. You also have to ensure that you get the ClientID and redirecturl set corectly

     

    The example I use goes like this

     

    string connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["Connect"].ConnectionString;

    using (HttpClient client = EnhancedQuickStart.SampleHelpers.GetHttpClient(connectionString, EnhancedQuickStart.SampleHelpers.clientId, EnhancedQuickStart.SampleHelpers.redirectUrl))

    {

           // -------------------------------------------

           // Put your stuff here

           // -------------------------------------------

    }

     

    The Connection String looks like this

     

    <add name="Connect" connectionString="Url=https://XXXXXXX.api.crm??.dynamics.com;Username=*****@*****;Password=**;" />

     

    The Components that I store in a separate class look like this

     

            public static string clientId = "******************************";

            public static string redirectUrl = "app://******************************";

     

            /// <summary>

            /// Method used to get a value from the connection string

            /// </summary>

            /// <param name="connectionString"></param>

            /// <param name="parameter"></param>

            /// <returns>The value from the connection string that matches the parameter key value</returns>

            public static string GetParameterValueFromConnectionString(string connectionString, string parameter)

            {

                try

                {

                    return connectionString.Split(';').Where(s => s.Trim().StartsWith(parameter)).FirstOrDefault().Split('=')[1];

                }

                catch (Exception)

                {

                    return string.Empty;

                }

            }

     

            /// <summary>

            /// Returns an HttpClient configured with an OAuthMessageHandler

            /// </summary>

            /// <param name="connectionString">The connection string to use.</param>

            /// <param name="clientId">The client id to use when authenticating.</param>

            /// <param name="redirectUrl">The redirect Url to use when authenticating</param>

            /// <param name="version">The version of Web API to use. Defaults to version 9.2 </param>

            /// <returns>An HttpClient you can use to perform authenticated operations with the Web API</returns>

            public static HttpClient GetHttpClient(string connectionString, string clientId, string redirectUrl, string version = "v9.2")

            {

                string url = GetParameterValueFromConnectionString(connectionString, "Url");

                string username = GetParameterValueFromConnectionString(connectionString, "Username");

                string password = GetParameterValueFromConnectionString(connectionString, "Password");

                try

                {

                    HttpMessageHandler messageHandler = new OAuthMessageHandler(url, clientId, redirectUrl, username, password,

                                  new HttpClientHandler());

     

                    HttpClient httpClient = new HttpClient(messageHandler)

                    {

                        BaseAddress = new Uri(string.Format("{0}/api/data/{1}/", url, version)),

     

                        Timeout = new TimeSpan(0, 2, 0)  //2 minutes

                    };

     

                    return httpClient;

                }

                catch (Exception)

                {

                    throw;

                }

            }

     

            /// <summary> Displays exception information to the console. </summary>

            /// <param name="ex">The exception to output</param>

            public static void DisplayException(Exception ex)

            {

                Console.WriteLine("The application terminated with an error.");

                Console.WriteLine(ex.Message);

                while (ex.InnerException != null)

                {

                    Console.WriteLine("\t* {0}", ex.InnerException.Message);

                    ex = ex.InnerException;

                }

            }

     

            private AuthenticationHeaderValue authHeader;

     

            public OAuthMessageHandler(string serviceUrl, string clientId, string redirectUrl, string username, string password,

                    HttpMessageHandler innerHandler)

                : base(innerHandler)

            {

     

                string apiVersion = "9.2";

                string webApiUrl = $"{serviceUrl}/api/data/v{apiVersion}/";

     

                //Build Microsoft.Identity.Client (MSAL) OAuth Token Request

                var authBuilder = PublicClientApplicationBuilder.Create(clientId)

                                .WithAuthority(AadAuthorityAudience.AzureAdMultipleOrgs)

                                .WithRedirectUri(redirectUrl)

                                .Build();

                var scope = serviceUrl + "//.default";

                string[] scopes = { scope };

     

                AuthenticationResult authBuilderResult;

                if (username != string.Empty && password != string.Empty)

                {

                    //Make silent Microsoft.Identity.Client (MSAL) OAuth Token Request

                    var securePassword = new SecureString();

                    foreach (char ch in password) securePassword.AppendChar(ch);

                    authBuilderResult = authBuilder.AcquireTokenByUsernamePassword(scopes, username, securePassword)

                                .ExecuteAsync().Result;

                }

                else

                {

                    //Popup authentication dialog box to get token

                    authBuilderResult = authBuilder.AcquireTokenInteractive(scopes)

                                .ExecuteAsync().Result;

                }

     

                //Note that an Azure AD access token has finite lifetime, default expiration is 60 minutes.

                authHeader = new AuthenticationHeaderValue("Bearer", authBuilderResult.AccessToken);

            }

     

            protected override Task<HttpResponseMessage> SendAsync(

                      HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)

            {

                request.Headers.Authorization = authHeader;

                return base.SendAsync(request, cancellationToken);

            }

     

     

    Paul Smith
    Business Systems and Integration Analyst
    SSAFA, the Armed Forces charity. Queen Elizabeth House, 4 St Dunstan's Hill, London. EC3R 8AD
    T: 
    +44 20 7463 9363
    E: 
    Paul.Smith@ssafa.org.uk
    W: 
    www.ssafa.org.uk
    The information in this email is confidential and may be legally privileged. It is intended solely for the addressee. Access to this e-mail by anyone else is unauthorised. If you are not the intended recipient, any disclosure, copying, distribution or any action taken or omitted to be taken in reliance on it, is prohibited and may be unlawful. Registered Charity No. 210760 Est. 1885. Registered charity in England and Wales No. 210760, in Scotland No. SC038056 and in Republic of Ireland No. 20202001. Est. 1885.





  • 4.  RE: Custom Web Service No Longer Connecting to Dynamics CRM

    SILVER CONTRIBUTOR
    Posted May 12, 2022 06:48 AM
    Looks like your method of authentication is using a deprecated approach(username and password). You need to use a modern authentication method to connect. Microsoft recently enforced this change. 
    The information contained in this message is proprietary and/or confidential. If you are not the intended recipient, please: (i) delete the message and all copies; (ii) do not disclose, distribute or use the message in any manner; and (iii) notify the sender immediately. In addition, please be aware that any message addressed to our domain is subject to archiving and review by persons other than the intended recipient. Thank you.





  • 5.  RE: Custom Web Service No Longer Connecting to Dynamics CRM

    GOLD CONTRIBUTOR
    Posted May 12, 2022 08:50 AM

    HI @Jim Field,

    Your client should have received from e-mails from Microsoft warning that this connection method has been deprecated. I received several warning me it was going into effect. I believe your short-term quick-fix will be as easy as a change in the connection string as documented here: Custom Web Service No Longer Connecting to Dynamics CRM | Customer Engagement & Dynamics CRM Forum (crmug.com).

    While a lengthy and detailed article, I encourage you to create your own application registration (for security reasons) as documented. 



    ------------------------------
    Jeff Woodard
    Chief Technical Officer
    Transportation Financial Services, Inc.
    West Palm Beach FL
    ------------------------------



  • 6.  RE: Custom Web Service No Longer Connecting to Dynamics CRM

    TOP CONTRIBUTOR
    Posted May 17, 2022 09:35 AM
    Hi,

    Change the connection string AuthType to "oAuth" from "office365"
    <connectionStrings>
    <add name="xxxxxxxxx" connectionString="Url=https://xxxxxx.crm3.dynamics.com; Username=xxxxxx; Password=xxxxxx; AuthType = OAuth;AppId = 676ghj4a-79c3c-444d-90e2-hjhsdf70ke7; RedirectUri = https://login.microsoftonline.com/common/oauth2/nativeclient;LoginPrompt=Never" />
    </connectionStrings>

    Inside the helper class where connection string is used

    private CrmServiceClient GetOrgServiceProxy()
    {
    try
    {
    ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
    String crmConnectionString = ConfigurationManager.ConnectionStrings["xxxxxxx"].ConnectionString;
    CrmServiceClient conn = new CrmServiceClient(crmConnectionString);


    if (conn != null)
    {
    if (conn.IsReady)
    {
    //IOrganizationService orgService = (IOrganizationService)conn.OrganizationWebProxyClient != null ? (IOrganizationService)conn.OrganizationWebProxyClient : (IOrganizationService)conn.OrganizationServiceProxy;
    //OrganizationServiceProxy orgService = conn.OrganizationServiceProxy;
    //orgService.EnableProxyTypes();
    return conn;
    }
    }
    else
    {
    const string UNABLE_TO_LOGIN_ERROR = "Unable to Login to Microsoft Dataverse";
    if (conn.LastCrmError.Equals(UNABLE_TO_LOGIN_ERROR))
    {
    Console.WriteLine("Check the connection string values in cds/App.config.");
    throw new Exception(conn.LastCrmError);
    }
    else
    {
    throw conn.LastCrmException;
    }
    }

    return conn;

    }
    catch (Exception ex)
    {

    throw;

    }


    }

    then you are good to go.

    Regards
    Jeebo

    ------------------------------
    Jeebo Mathews
    Agricorp
    ------------------------------



If you've found this thread useful, dive deeper into User Group community content by role