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.

Remy van Duijkeren

Remy van Duijkeren

Power Platform Advisor

Microsoft Power Platform Advisor with over 25 years of experience in IT with a focus on (marketing) automation and integration.

Helping organizations with scaling their business by automating processes on the Power Platform (Dynamics 365).

Expert in Power Platform, Dynamics 365 (Marketing & Sales) and Azure Integration Services. Giving advice and strategy consultancy.

Services:
– Strategy and tactics advise
– Automating and integrating

Subscribe to
The Daily Friction

A daily newsletter on automation and eliminating friction

Related Content

Improving the Form Switcher

Improving the Form Switcher

In Microsoft Dynamics 365, Model Driven Apps in the Power Platform, you can create multiple main forms for the same table or TOFKAE (The Object Formally Known As Entity). When to use multiple forms This can be handy because you can assign different security roles to...

read more
How to use Environment Variables in your plugins

How to use Environment Variables in your plugins

Environment Variables Dynamics 365 / Power Apps solution can have Environment Variables. Often a Settings table (=entity) would be created to store configuration settings that differ between environments. We now can replace these with environment variables. These...

read more
The attribute was not found in the MetadataCache

The attribute was not found in the MetadataCache

The Problem I was trying to export a Solution from Dynamics 365 CE / Dynamics CRM to deploy into the next environment, but instead I got a Query Builder Error: The specified field does not exist in Microsoft Dynamics 365. Looking into the log file I saw the...

read more