External authentication with Dataverse ServiceClient

by Jul 1, 2024Blog

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.

Remy van Duijkeren

Remy van Duijkeren

Power Platform Automator

Automation and integration aren’t just what I do—they’re what I do best.

I help businesses work smarter by removing friction, automating what slows them down, and connecting systems that should’ve been talking all along.

 

Get My Thoughts on Automation & Development

Join my personal newsletter and get practical insights on building faster, integrating smarter, and removing friction in IT systems — especially on Power Platform and Dynamics 365.

Related