Write client app authentication code

After you set up an Azure Digital Twins instance and authentication, you can create a client application that you'll use to interact with the instance. Once you have set up a starter client project, you'll need to write code in that client app to authenticate it against the Azure Digital Twins instance.

Azure Digital Twins authenticates using Microsoft Entra Security Tokens based on OAUTH 2.0. To authenticate your SDK, you'll need to get a bearer token with the right permissions to Azure Digital Twins, and pass it along with your API calls.

This article describes how to obtain credentials using the Azure.Identity client library. While this article shows code examples in C#, such as what you'd write for the .NET (C#) SDK, you can use a version of Azure.Identity regardless of what SDK you're using (for more on the SDKs available for Azure Digital Twins, see Azure Digital Twins APIs and SDKs.

Prerequisites

First, complete the setup steps in Set up an instance and authentication. This setup will ensure you have an Azure Digital Twins instance and your user has access permissions. After that setup, you're ready to write client app code.

To continue, you'll need a client app project in which you write your code. If you don't already have a client app project set up, create a basic project in your language of choice to use with this tutorial.

Authenticate using Azure.Identity library

Azure.Identity is a client library that provides several credential-obtaining methods that you can use to get a bearer token and authenticate with your SDK. Although this article gives examples in C#, you can view Azure.Identity for several languages, including...

Three common credential-obtaining methods in Azure.Identity are:

  • DefaultAzureCredential provides a default TokenCredential authentication flow for applications that will be deployed to Azure, and is the recommended choice for local development. It also can be enabled to try the other two methods recommended in this article; it wraps ManagedIdentityCredential and can access InteractiveBrowserCredential with a configuration variable.
  • ManagedIdentityCredential works well in cases where you need managed identities (MSI), and is a good candidate for working with Azure Functions and deploying to Azure services.
  • InteractiveBrowserCredential is intended for interactive applications, and can be used to create an authenticated SDK client.

The rest of this article shows how to use these methods with the .NET (C#) SDK.

Add Azure.Identity to your .NET project

To set up your .NET project to authenticate with Azure.Identity, complete the following steps:

  1. Include the SDK package Azure.DigitalTwins.Core and the Azure.Identity package in your project. Depending on your tools of choice, you can include the packages using the Visual Studio package manager or the dotnet command-line tool.

  2. Add the following using statements to your project code:

    using Azure.DigitalTwins.Core;
    using Azure.Identity;
    using System;
    

Next, add code to obtain credentials using one of the methods in Azure.Identity. The following sections give more detail about using each one.

DefaultAzureCredential method

DefaultAzureCredential provides a default TokenCredential authentication flow for applications that will be deployed to Azure, and is the recommended choice for local development.

To use the default Azure credentials, you'll need the Azure Digital Twins instance's URL (instructions to find).

Here's a code sample to add a DefaultAzureCredential to your project:

public class DefaultAzureCredentialSample
{
    // The URL of your instance, starting with the protocol (https://)
    private const string adtInstanceUrl = "https://<your-Azure-Digital-Twins-instance-URL>";

    internal void RunSample()
    {
        //...

        DigitalTwinsClient client;
        try
        {
            var credential = new DefaultAzureCredential();
            client = new DigitalTwinsClient(new Uri(adtInstanceUrl), credential);
        }
        catch (Exception e)
        {
            Console.WriteLine($"Authentication or client creation error: {e.Message}");
            Environment.Exit(0);
        }
    }
}

Note

There's currently a known issue affecting the DefaultAzureCredential wrapper class that may result in an error while authenticating. If you encounter this issue, you can try instantiating DefaultAzureCredential with the following optional parameter to resolve it: new DefaultAzureCredential(new DefaultAzureCredentialOptions { ExcludeSharedTokenCacheCredential = true });

For more information about this issue, see Azure Digital Twins known issues.

Set up local Azure credentials

With DefaultAzureCredential, the sample will search for credentials in your local environment, like an Azure sign-in in a local Azure CLI or in Visual Studio or Visual Studio Code. For this reason, you should sign in to Azure locally through one of these mechanisms to set up credentials for the sample.

If you're using Visual Studio or Visual Studio Code to run code samples, make sure you're signed in to that editor with the same Azure credentials that you want to use to access your Azure Digital Twins instance. If you're using a local CLI window, run the az login command to sign in to your Azure account. After this, when you run your code sample, you should be authenticated automatically.

ManagedIdentityCredential method

The ManagedIdentityCredential method works well in cases where you need managed identities (MSI)—for example, when authenticating with Azure Functions.

This means that you can use ManagedIdentityCredential in the same project as DefaultAzureCredential or InteractiveBrowserCredential, to authenticate a different part of the project.

To use the default Azure credentials, you'll need the Azure Digital Twins instance's URL (instructions to find). You may also need an app registration and the registration's Application (client) ID.

In an Azure function, you can use the managed identity credentials like this:

public class ManagedIdentityCredentialSample
{
    // The URL of your instance, starting with the protocol (https://)
    private const string adtInstanceUrl = "https://<your-Azure-Digital-Twins-instance-URL>";

    internal void RunSample()
    {
        DigitalTwinsClient client;
        try
        {
            // To use the function app's system-assigned identity:
            ManagedIdentityCredential cred = new ManagedIdentityCredential();
            // To use a user-assigned identity for the function app:
            //ManagedIdentityCredential cred = new ManagedIdentityCredential("<uai-client-ID>");

            client = new DigitalTwinsClient(new Uri(adtInstanceUrl), cred);
        }
        catch (Exception e)
        {
            Console.WriteLine($"Authentication or client creation error: {e.Message}");
            Environment.Exit(0);
        }
    }
}

While creating the credential, leaving the parameter empty as shown above will return the credential for the function app's system-assigned identity, if it has one. To specify a user-assigned identity instead, pass the user-assigned identity's client ID into the parameter.

InteractiveBrowserCredential method

The InteractiveBrowserCredential method is intended for interactive applications and will bring up a web browser for authentication. You can use this method instead of DefaultAzureCredential in cases where you require interactive authentication.

To use the interactive browser credentials, you'll need an app registration that has permissions to the Azure Digital Twins APIs. For steps on how to set up this app registration, see Create an app registration with Azure Digital Twins access. Once the app registration is set up, you'll need...

Here's an example of the code to create an authenticated SDK client using InteractiveBrowserCredential.

public class InteractiveBrowserCredentialSample
{
    // Your client / app registration ID
    private const string clientId = "<your-client-ID>";
    // Your tenant / directory ID
    private const string tenantId = "<your-tenant-ID>";
    // The URL of your instance, starting with the protocol (https://)
    private const string adtInstanceUrl = "https://<your-Azure-Digital-Twins-instance-URL>";

    internal void RunSample()
    {
        //...

        DigitalTwinsClient client;
        try
        {
            var credential = new InteractiveBrowserCredential(tenantId, clientId);
            client = new DigitalTwinsClient(new Uri(adtInstanceUrl), credential);
        }
        catch (Exception e)
        {
            Console.WriteLine($"Authentication or client creation error: {e.Message}");
            Environment.Exit(0);
        }
    }
}

Note

While you can place the client ID, tenant ID and instance URL directly into the code as shown above, it's a good idea to have your code get these values from a configuration file or environment variable instead.

Authenticate Azure Functions

This section contains some of the important configuration choices in the context of authenticating with Azure Functions. First, you'll read about recommended class-level variables and authentication code that will allow the function to access Azure Digital Twins. Then, you'll read about some final configuration steps to complete for your function after its code is published to Azure.

Write application code

When writing the Azure function, consider adding these variables and code to your function:

  • Code to read the Azure Digital Twins service URL as an environment variable or configuration setting. It's a good practice to read the service URL from an application setting/environment variable, rather than hard-coding it in the function. In an Azure function, that code to read the environment variable might look like this:

    private static readonly string adtInstanceUrl = Environment.GetEnvironmentVariable("ADT_SERVICE_URL");
    

    Later, after publishing the function, you'll create and set the value of the environment variable for this code to read. For instructions on how to do so, skip ahead to Configure application settings.

  • A static variable to hold an HttpClient instance. HttpClient is relatively expensive to create, so you'll probably want to create it once with the authentication code to avoid creating it for every function invocation.

    private static readonly HttpClient singletonHttpClientInstance = new HttpClient();
    
  • Managed identity credential. Create a managed identity credential that your function will use to access Azure Digital Twins.

    // To use the function app's system-assigned identity:
    var cred = new ManagedIdentityCredential();
    // To use a user-assigned identity for the function app:
    //var cred = new ManagedIdentityCredential("<uai-client-ID>");
    

    Leaving the parameter empty as shown above will return the credential for the function app's system-assigned identity, if it has one. To specify a user-assigned identity instead, pass the user-assigned identity's client ID into the parameter.

    Later, after publishing the function, you'll make sure the function's identity has permission to access the Azure Digital Twins APIs. For instructions on how to do so, skip ahead to Assign an access role.

  • A local variable DigitalTwinsClient. Add the variable inside your function to hold your Azure Digital Twins client instance. Don't make this variable static inside your class.

    var client = new DigitalTwinsClient(
        new Uri(adtInstanceUrl),
        cred,
        new DigitalTwinsClientOptions
        {
            Transport = new HttpClientTransport(singletonHttpClientInstance)
        });
    
  • A null check for adtInstanceUrl. Add the null check and then wrap your function logic in a try/catch block to catch any exceptions.

After these variables are added to a function, your function code might look like the following example.

// Default URL for triggering event grid function in the local environment.
// http://localhost:7071/runtime/webhooks/EventGrid?functionName={functionname}
//<Function_dependencies>
using Azure.Core.Pipeline;
using Azure.DigitalTwins.Core;
using Azure.Identity;
using Azure.Messaging.EventGrid;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.EventGrid;
using Microsoft.Extensions.Logging;
using System;
using System.Net.Http;
//</Function_dependencies>

namespace DigitalTwins_Samples
{
    public class DigitalTwinsIngestFunctionSample
    {
        // Your Digital Twin URL is stored in an application setting in Azure Functions
        // <ADT_service_URL>
        private static readonly string adtInstanceUrl = Environment.GetEnvironmentVariable("ADT_SERVICE_URL");
        // </ADT_service_URL>
        // <HTTP_client>
        private static readonly HttpClient singletonHttpClientInstance = new HttpClient();
        // </HTTP_client>

        [FunctionName("TwinsFunction")]
        public void Run([EventGridTrigger] EventGridEvent eventGridEvent, ILogger log)
        {
            log.LogInformation(eventGridEvent.Data.ToString());
            if (adtInstanceUrl == null) log.LogError("Application setting \"ADT_SERVICE_URL\" not set");
            try
            {
                // Authenticate with Digital Twins
                // <ManagedIdentityCredential>
                // To use the function app's system-assigned identity:
                var cred = new ManagedIdentityCredential();
                // To use a user-assigned identity for the function app:
                //var cred = new ManagedIdentityCredential("<uai-client-ID>");
                // </ManagedIdentityCredential>
                // <DigitalTwinsClient>
                var client = new DigitalTwinsClient(
                    new Uri(adtInstanceUrl),
                    cred,
                    new DigitalTwinsClientOptions
                    {
                        Transport = new HttpClientTransport(singletonHttpClientInstance)
                    });
                // </DigitalTwinsClient>
                log.LogInformation($"ADT service client connection created.");

                // Add your business logic here.
            }
            catch (Exception e)
            {
                log.LogError(e.Message);
            }
        }
    }
}

When you're finished with your function code, including adding authentication and the function's logic, publish the app to Azure

Configure published app

Finally, complete the following configuration steps for a published Azure function to make sure it can access your Azure Digital Twins instance.

Run the following commands in Azure Cloud Shell or a local Azure CLI.

Note

This section must be completed by an Azure user who has permissions to manage user access to Azure resources, including granting and delegating permissions. Common roles that meet this requirement are Owner, Account admin, or the combination of User Access Administrator and Contributor. For more information about permission requirements for Azure Digital Twins roles, see Set up an instance and authentication.

Assign an access role

The Azure function requires a bearer token to be passed to it. To make sure the bearer token is passed, grant the function app the Azure Digital Twins Data Owner role for your Azure Digital Twins instance, which will give the function app permission to perform data plane activities on the instance.

  1. Use the following command to create a system-managed identity for your function (if the function already has one, this command will print its details). Take note of the principalId field in the output. You'll use this ID to refer to the function so that you can grant it permissions in the next step.

    az functionapp identity assign --resource-group <your-resource-group> --name <your-function-app-name>	
    
  2. Use the principalId value in the following command to give the function the Azure Digital Twins Data Owner role for your Azure Digital Twins instance.

    az dt role-assignment create --dt-name <your-Azure-Digital-Twins-instance> --assignee "<principal-ID>" --role "Azure Digital Twins Data Owner"
    

Configure application settings

Next, make the URL of your Azure Digital Twins instance accessible to your function by setting an environment variable for it.

Tip

The Azure Digital Twins instance's URL is made by adding https:// to the beginning of your instance's host name. To see the host name, along with all the properties of your instance, run az dt show --dt-name <your-Azure-Digital-Twins-instance>.

The following command sets an environment variable for your instance's URL that your function will use whenever it needs to access the instance.

az functionapp config appsettings set --resource-group <your-resource-group> --name <your-function-app-name> --settings "ADT_SERVICE_URL=https://<your-Azure-Digital-Twins-instance-host-name>"

Authenticate across tenants

Azure Digital Twins is a service that only supports one Microsoft Entra tenant: the main tenant from the subscription where the Azure Digital Twins instance is located.

As a result, requests to the Azure Digital Twins APIs require a user or service principal that is a part of the same tenant where the Azure Digital Twins instance resides. To prevent malicious scanning of Azure Digital Twins endpoints, requests with access tokens from outside the originating tenant will be returned a "404 Sub-Domain not found" error message. This error will be returned even if the user or service principal was given an Azure Digital Twins Data Owner or Azure Digital Twins Data Reader role through Microsoft Entra B2B collaboration.

If you need to access your Azure Digital Twins instance using a service principal or user account that belongs to a different tenant from the instance, you can have each federated identity from another tenant request a token from the Azure Digital Twins instance's "home" tenant.

One way to do this is with the following CLI command, where <home-tenant-ID> is the ID of the Microsoft Entra tenant that contains the Azure Digital Twins instance:

az account get-access-token --tenant <home-tenant-ID> --resource https://digitaltwins.azure.net

After requesting this, the identity will receive a token issued for the https://digitaltwins.azure.net Microsoft Entra resource, which has a matching tenant ID claim to the Azure Digital Twins instance. Using this token in API requests or with your Azure.Identity code should allow the federated identity to access the Azure Digital Twins resource.

You can also specify the home tenant in the credential options in your code.

The following example shows how to set a sample tenant ID value for InteractiveBrowserTenantId in the DefaultAzureCredential options:

public class DefaultAzureCredentialOptionsSample
{
    // The URL of your instance, starting with the protocol (https://)
    private const string adtInstanceUrl = "https://<your-Azure-Digital-Twins-instance-URL>";

    private static DefaultAzureCredentialOptions credentialOptions = new DefaultAzureCredentialOptions()
    {
        ExcludeSharedTokenCacheCredential = true,
        ExcludeVisualStudioCodeCredential = true,
        TenantId = "<your-Azure-Active-Directory-tenant-ID>"
    };

    private static DefaultAzureCredential credential = new DefaultAzureCredential(credentialOptions);

    DigitalTwinsClient client = new DigitalTwinsClient(new Uri(adtInstanceUrl), credential);
}

There are similar options available to set a tenant for authentication with Visual Studio and Visual Studio Code. For more information on the options available, see the DefaultAzureCredentialOptions documentation.

Other credential methods

If the highlighted authentication scenarios above don't cover the needs of your app, you can explore other types of authentication offered in the Microsoft identity platform. The documentation for this platform covers more authentication scenarios, organized by application type.

Next steps

Read more about how security works in Azure Digital Twins:

Or, now that authentication is set up, move on to creating and managing models in your instance: