External authentication with Dataverse ServiceClient

by Jul 1, 2024

For a while now we can use the new Dataverse ServiceClient that replaced the old CrmServiceClient. It has three big improvements:

  1. Works for .NET 5.0 and up (.NET Core)
  2. Uses the newer MSAL.NET instead of ADAL.NET (which is out of support) for authentication
  3. Support for async (IOrganizationServiceAsync and IOrganizationServiceAsync2)

Migration is quite easy because the interface of the client is the same.

The default way to create a ServiceClient is by giving it a connection string, like:

ServiceClient serviceClient = 
        new(Configuration.GetConnectionString("myConnection"));

Windows desktop app

But what if you want to use the ServiceClient in a Windows desktop app?

Things become more complicated, because you want to authenticate the user (behalf of flow) and you don’t want to store a connecting string with secrets on the users computer.

ServiceClient can manage access tokens for authenticate internaly, but what if you already managing access tokens in the app and you don’t want ServiceClient do that?

ServiceClient has an option to configure it with redirect authentication to an external function. This function that will be called when the access token is require for interaction with Dataverse.

This function must accept a string (InstanceURI) and return a string (accesstoken), like Func<String,Task<String>>

You can use the specific constructor to do this, but I prefer to use new ConnectionOptions object in this case, like this pseudo code:

static readonly Lazy<IOrganizationService> s_xrmClient = new Lazy<IOrganizationService>(() =>
{
    // Connect using OAuth where access is not managed internally by ServiceClient,
    // but external using an access token provider function
    var connectionOptions = new ConnectionOptions
    {
        AuthenticationType = AuthenticationType.OAuth,
        ServiceUri = new Uri("https://org.crm4.dynamics.com"),
        AccessTokenProviderFunctionAsync = RequestAccessToken
    };

    return new ServiceClient(connectionOptions);

    // Inline method: Retrieve an access token using the registered AccessTokenProvider
    Task<string> RequestAccessToken(string instanceUri)
    {
        var tokenProvider = Host.Services.GetRequiredService<IAccessTokenProvider>();

        return tokenProvider.GetAuthorizationTokenAsync(new Uri(instanceUri));
    }
});

MSAL.NET uses scopes to request access, where the instanceUri will be the scope. Keep in mind that you need to add /.default for the scope to be correct, like:

if (uri != null)
{
    if (!AllowedHostsValidator.IsUrlHostValid(uri))
        return null;

    scopes = new[] { $"{uri.Scheme}://{uri.Host}/.default" };
}

By configuring your ServiceClient like this, you are delegating the authentication to an external caller.

Subscribe to
The Daily Friction

A daily newsletter on automation and eliminating friction

Related Content

Everyone got ALM wrong in Dynamics 365 / Dataverse

For ages, we've been ferociously encouraging the integration of developer practices, such as source control and ALM, into the Dynamics 365/Dataverse realm. The ultimate truth The revered 'Master Branch' in source control, has always been the sole fountainhead from...

read more
Early-Bound Classes for .NET 4.6.2 and 6.0

Early-Bound Classes for .NET 4.6.2 and 6.0

You like to use strong types in .NET when working with Dataverse / Dynamics 365? Are you into Early-Bound Classes? Generating entity classes? You can use CrmSvcUtil for this, but I personally like to use XrmContext from Delegate to do this, because it creates smaller...

read more
WordPress Blocks Post using the REST API

WordPress Blocks Post using the REST API

Sending email to my subscribers is done by an automation flow. In the last couple of days paragraphs in my emails started to disappear. It changed into one large blob of text which is very annoying to read. Sorry about this! The automation depends on the RSS-feed of...

read more