web
You’re offline. This is a read only version of the page.
close
Skip to main content

Notifications

Announcements

No record found.

Community site session details

Community site session details

Session Id :
Microsoft Dynamics CRM (Archived)

Using xrm.tooling for silent login

(0) ShareShare
ReportReport
Posted on by

Hi,

Is there any way to use Xrm.Tooling to do a silent login using only an org URL and not the service URL of a specific service (i.e. the web api or ye olde org service). There's been talk that the tooling should smoothly transition to the web api for connecting to the CR... eehh.. CE.

I've successfully authenticated to AAD with an application user and a certificate, the thing I'd like to do is to use the Xrm.Tooling for the integration since it's what Microsoft seems to be promoting at the moment. At the same time I don't want to point to a specific service endpoint but rather to the CRM instance since we don't know which endpoint will be here next week.

Is this possible?

Regards

*This post is locked for comments

I have the same question (0)
  • Suggested answer
    Michel van den Brink Profile Picture
    4,697 on at

    Hello,

    Yes, if you use the CrmServiceClient from the XrmTooling as a base (which implements IOrganizationService like any other implementation) you will automatically transition to the best available endpoint.

      

    This method only works for authentications with an Application User:

    You need to create a class Microsoft.Xrm.Tooling.Connector.IOverrideAuthHookWrapper implementation of and then add it to static property CrmServiceClient.AuthOverrideHook

    IOverrideAuthHookWrapper defines 1 method, for obtaining the access token: string GetAuthToken (Uri connectedUri);

      

    Reference:

    https://docs.microsoft.com/en-us/dotnet/api/microsoft.xrm.tooling.connector.crmserviceclient.authoverridehook?view=dynamics-xrmtooling-ce-9#Microsoft_Xrm_Tooling_Connector_CrmServiceClient_AuthOverrideHook 

      

    Here's an example, I've also posted one with syntax highlighting to GitHub:

    https://gist.github.com/justlikeed/5f605cfac460c4ce039811ce6d19bf6a 

       

    using Microsoft.IdentityModel.Clients.ActiveDirectory;
    using System;
    using System.Collections.Generic;
    
    namespace MichelOverrideExampleApp
    {
        public class MichelOverrideExampleHookImplementation : Microsoft.Xrm.Tooling.Connector.IOverrideAuthHookWrapper
        {
            // In memory cache of access tokens
            Dictionary<string, AuthenticationResult> accessTokens = new Dictionary<string, Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult>();
    
            public void AddAccessToken(Uri orgUri, Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult accessToken)
            {
                // Access tokens can be matched on the hostname,
                // different endpoints in the same organization can use the same access token
                accessTokens[orgUri.Host] = accessToken;
            }
    
            public string GetAuthToken(Uri connectedUri)
            {
                // Check if you have an access token for this host
                if (accessTokens.ContainsKey(connectedUri.Host) && accessTokens[connectedUri.Host].ExpiresOn > DateTime.Now)
                {
                    return accessTokens[connectedUri.Host].AccessToken;
                }
                else
                {
                    accessTokens[connectedUri.Host] = GetAccessTokenFromAzureAD(connectedUri);
                }
                return null;
            }
    
            private AuthenticationResult GetAccessTokenFromAzureAD(Uri orgUrl)
            {
                // TODO: Add your authentication implementation here
                return null;
            }
        }
    
        public class MichelOverrideExampleProgram
        {
            static void Main(string[] args)
            {
                // Define organization Url
                Uri orgUrl = new Uri("https://organizationname.crm.dynamics.com");
    
                // Call your existing authentication implementation
                Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult accessToken = GetAccessTokenFromAzureAD(orgUrl);
    
                // Create instance of your hook
                var hook = new MichelOverrideExampleHookImplementation();
                // Add token to your hook
                hook.AddAccessToken(orgUrl, accessToken);
    
                // Register the hook with the CrmServiceClient
                Microsoft.Xrm.Tooling.Connector.CrmServiceClient.AuthOverrideHook = hook;
    
                // Create a new instance of CrmServiceClient, pass your organization url and make sure useUniqueInstance = true!
                var client = new Microsoft.Xrm.Tooling.Connector.CrmServiceClient(orgUrl, useUniqueInstance: true);
                if (!client.IsReady)
                {
                    // Connection failed, report error
                    Console.Error.WriteLine(client.LastCrmException?.Message ?? client.LastCrmError);
                }
                else
                {
                    // Connection success
                    // TODO Add your code here
                    //client.Retrieve("account", Guid.Empty, new ColumnSet("accountid"));
                }
            }
    
            private static AuthenticationResult GetAccessTokenFromAzureAD(Uri orgUrl)
            {
                // TODO: Add your authentication implementation here
                return null;
            }
        }
    
    }

      

    Hope this helps you get started. If you have any other questions, please let me know!

    If you found my answer helpful, please help the community by marking it as verified :-)

  • Rickard Norström Profile Picture
    on at

    Michel,

    This is great stuff but I can't get a client. I changed the "ExpiresOn < DateTime.Now" to "ExpiresOn > DateTime.Now", that made more sence, I hope that didn't break things but it seems that a token that's just created should be fine.

    I'm also using an orgUrl with https, I suppose that's just a typo.

    The error I'm getting is "Unable to Login to Dynamics CRMOrganizationServiceProxy is null". I have a bearertoken from that instance I'm trying to log in to so I'm a bit confused here

    Any ideas?

  • Michel van den Brink Profile Picture
    4,697 on at

    Hello Rickard,

    You are right about the typos.

    For the error you are getting, are you using the latest version of the Microsoft.CrmSdk.XrmTooling.CoreAssembly, 9.0.0.7? And other packages of the CrmSdk, everything should be 9.0.07 (or higher, if you're reading this by the time an update has been released).

    https://www.nuget.org/packages/Microsoft.CrmSdk.XrmTooling.CoreAssembly/ 

  • Rickard Norström Profile Picture
    on at

    Hi Michel,

    True, I was sloppy in regard of the version, that has been fixed now :) Still the same problem though, I can get the token using AcquireTokenAsync(<orgUrl>, ClientAssertionCertificate) but not connect to the CRM (CE)

    I updated the Microsoft.IdentityModel.Clients.ActiveDirectory to v3.something too, I know there's been talk about ADAL > 2 isn't supported but since I can get the bearer token using 3.something that shouldn't be the issue imho.

    Very strange.

    Update, I've also tried to connect to the OData endpoint using the token I got using the certificate and I can do both GET and POST with that login so it really shouldn't be the token/authentication that's wrong. I also tried Marius Pedersen's version from https://www.crmviking.com/2018/03/dynamics-365-s2s-oauth-authentication.html with the same result, this is very annoying.

  • Suggested answer
    Michel van den Brink Profile Picture
    4,697 on at

    Ah yes, the Microsoft.IdentityModel.Clients.ActiveDirectory must stay on major v2, and can't be updated to major v3

    The team behind that package decided to introduce a breaking change in v3.

    For this package we have to consider v2 and v3 being two separate branches for the same product, both are still being updated, it's a strange choose but, we're going to have to live with it.

  • Rickard Norström Profile Picture
    on at

    Same result, not able to get a client. My code below, I don' really understand what's wrong since I get a token from the AcquireToken/AcquireTokenAsync (the async is needed for ADAL 3, they removed the sync one but it becomes sync when you ask for the result :)) Other than that, it's your code above.

    I'm very grateful that you're helping me here, it would be awesome to get this working, even though I still need to provide a password which needs to be stored somewhere. What I really want to do is to connect without pointing out a specific service endpoint since they will change.

    class Program
        {
            static void Main(string[] args)
            {

                string aadInstance = "login.microsoftonline.com";
                string tenantID = "crmktest1.onmicrosoft.com";
                X509Certificate2 cert = new X509Certificate2(@"C:\Users\Rickard\Documents\sshcert\pkcs12.der","Coolpwd");
               

                AuthenticationContext ac = new AuthenticationContext(aadInstance + tenantID);
                ClientAssertionCertificate cac = new ClientAssertionCertificate(<client id>, cert);
                AuthenticationResult ar = ac.AcquireTokenAsync("crmktest1.crm4.dynamics.com", cac).Result;
                int i = 0;
              
                var hook = new AuthInsanity();
                hook.AddAccessToken(new Uri("crmktest1.crm4.dynamics.com"), ar);
                CrmServiceClient.AuthOverrideHook = hook;
               
                CrmServiceClient client = new CrmServiceClient(new Uri("crmktest1.crm4.dynamics.com"), useUniqueInstance:true);
                if(!client.IsReady)
                {
                    Console.WriteLine("blä");
                }
                else
                {
                    Console.WriteLine("connected");
                    WhoAmIRequest req = new WhoAmIRequest();
                    WhoAmIResponse resp = (WhoAmIResponse)client.Execute(req);
                    Console.WriteLine(resp.UserId);
                }
            }
            private AuthenticationResult GetAccessTokenFromAzureAD(Uri orgUrl)
            {
                string aadInstance = "login.microsoftonline.com";
                string tenantID = "crmktest1.onmicrosoft.com";
                string connString = "";
                X509Certificate2 cert = new X509Certificate2(@"C:\Users\Rickard\Documents\sshcert\pkcs12.der", "Coolpwd");

                AuthenticationContext ac = new AuthenticationContext(aadInstance + orgUrl);
                ClientAssertionCertificate cac = new ClientAssertionCertificate(<client id>, cert);
                AuthenticationResult ar = ac.AcquireTokenAsync("crmktest1.crm4.dynamics.com", cac).Result;

                return ar;
            }
        }

        public class AuthInsanity : IOverrideAuthHookWrapper
        {
            Dictionary<string, AuthenticationResult> accessTokens = new Dictionary<string, AuthenticationResult>();

            public void AddAccessToken(Uri orgUri, AuthenticationResult ar)
            {
                accessTokens[orgUri.Host] = ar;
            }
            public string GetAuthToken(Uri connectedUri)
            {
                // Check if you have an access token for this host
                if (accessTokens.ContainsKey(connectedUri.Host) && accessTokens[connectedUri.Host].ExpiresOn > DateTime.Now)
                {
                    return accessTokens[connectedUri.Host].AccessToken;
                }
                else
                {
                    accessTokens[connectedUri.Host] = GetAccessTokenFromAzureAD(connectedUri);
                }

                //probably not correct, guessing we should return the accesstoken above but that never happens in the first instance anyway....
                return null;

            }
            private AuthenticationResult GetAccessTokenFromAzureAD(Uri orgUrl)
            {
                string aadInstance = "login.microsoftonline.com";
                string tenantID = "crmktest1.onmicrosoft.com";
                string connString = "";
                X509Certificate2 cert = new X509Certificate2(@"C:\Users\Rickard\Documents\sshcert\pkcs12.der", "CoolPw");

                AuthenticationContext ac = new AuthenticationContext(aadInstance + tenantID);
                ClientAssertionCertificate cac = new ClientAssertionCertificate(<clientid>, cert);
                AuthenticationResult ar = ac.AcquireTokenAsync("crmktest1.crm4.dynamics.com", cac).Result;

                return ar;
            }
        }

  • Rickard Norström Profile Picture
    on at

    Back to TLS 1.2 issues. I did this in a .NET 4.7 project and need to add the following line to the App.Config:

    <runtime>

      <AppContextSwitchOverrides value="Switch.System.ServiceModel.DisableUsingServicePointManagerSecurityProtocols=false;Switch.System.Net.DontEnableSchUseStrongCrypto=false" />

    </runtime>

    Thanks for helping out Michel!

    Would you mind if I write this up in a blog and mention you and this thread?

  • AAVI21 Profile Picture
    25 on at

    Hi Michel,

    I have similar requirement and after referring above code , able to generate ADAL token. Now challenge is , after first time token generation via user credential, client id , I have to create CRM connection with the same token stored in app so that for subsequent request I can reuse the same. Can you please suggest , how to add token as input to below piece of code for getting CRM connection. Thanks in Advance.

     var adtoken = new XRMToolingToken();

                   adtoken.AddAccessToken(crmUrl, result);

                   CrmServiceClient.AuthOverrideHook = adtoken;

                   var conn = new CrmServiceClient(crmUrl, useUniqueInstance: true);

                   IOrganizationService _orgService = (IOrganizationService)conn.OrganizationWebProxyClient != null ? (IOrganizationService)conn.OrganizationWebProxyClient : (IOrganizationService)conn.OrganizationServiceProxy;

    public class XRMToolingToken : Microsoft.Xrm.Tooling.Connector.IOverrideAuthHookWrapper

           {

               Dictionary<string, AuthenticationResult> accessTokens = new Dictionary<string, AuthenticationResult>();

               public void AddAccessToken(Uri url, AuthenticationResult accessToken)

               {

                   accessTokens[url.Host] = accessToken;

               }

               public string GetAuthToken(Uri connectedUri)

               {

                   if (accessTokens.ContainsKey(connectedUri.Host) && accessTokens[connectedUri.Host].ExpiresOn > DateTime.Now)

                   {

                       return accessTokens[connectedUri.Host].AccessToken;

                   }

                   else

                   {

                       //accessTokens[connectedUri.Host] = GetAccessTokenFromAzureAD(connectedUri);

                   }

                   return null;

               }

           }

  • Rickard Norström Profile Picture
    on at

    AAVI21,

    You already have you client in the "conn" variable above. What happens if you use the code I put in above to try if you can do a whoamirequest.

    Regards

  • MattB-MSFT Profile Picture
    on at

    As a point...

    the Xrm.Tooling.Connector natively support Certificate based Auth. 

    You don't need to use the externalAuth mode to do it. 

    Example: 

    string ConnectionStr = "AuthType=Certificate;url=<url>;thumbprint=<CertThumbPrint>;AppId=<appID>;RequireNewInstance=true;SkipDiscovery=true";
    CrmServiceClient cli1 = new CrmServiceClient(ConnectionStr);

    if you don't want to use a connection string, you can also do it with a constructor . 

    CrmServiceClient client = new CrmServiceClient(null, StoreName.My, "<CertThumbPrint>", new Uri("<ServiceURLBase>/XRMServices/2011/Organization.svc"), false, null, <appID>, null, null);
    if (client.IsReady) { ... }

    MattB.

Under review

Thank you for your reply! To ensure a great experience for everyone, your content is awaiting approval by our Community Managers. Please check back later.

Helpful resources

Quick Links

Responsible AI policies

As AI tools become more common, we’re introducing a Responsible AI Use…

Neeraj Kumar – Community Spotlight

We are honored to recognize Neeraj Kumar as our Community Spotlight honoree for…

Leaderboard > 🔒一 Microsoft Dynamics CRM (Archived)

#1
SA-08121319-0 Profile Picture

SA-08121319-0 4

#1
Calum MacFarlane Profile Picture

Calum MacFarlane 4

#3
Alex Fun Wei Jie Profile Picture

Alex Fun Wei Jie 2

Last 30 days Overall leaderboard

Featured topics

Product updates

Dynamics 365 release plans