Use OAuth with Common Data Service

OAuth 2.0 is the industry-standard protocol for authorization. After people provide credentials to authenticate, OAuth determines whether they are authorized to access the resources.

Client applications must support the use of OAuth to access data using the Web API. OAuth enables two-factor authentication (2FA) or certificate-based authentication for server-to-server application scenarios.

OAuth requires an identity provider for authentication. For Common Data Service the identity provider is Azure Active Directory (AAD). To authenticate with AAD using a Microsoft work or school account, use the Azure Active Directory Authentication Libraries (ADAL).

Note

This topic will introduce common concepts related to connecting to Common Data Service using OAuth with the ADAL libraries. This content will focus on how a developer can connect to Common Data Service but not on the inner workings of OAuth or the ADAL libraries. For complete information related to authentication see the Azure Active Directory documentation. What is authentication? is a good place to start.

Samples we provide are pre-configured with appropriate registration values so that you can run them without generating your own app registration. When you publish your own apps, you must use your own registration values.

App Registration

When you connect using OAuth you must first register an application in your Azure AD tenant. How you should register your app depends on the type of app you want to make.

In all cases, start with basic steps to register an app described in the AAD topic: Quickstart: Register an app with the Azure Active Directory v1.0 endpoint. For Common Data Service specific instructions see Walkthrough: Register an app with Azure Active Directory > Create an application registration.

The decisions you will need to make in this step mostly depend on the Application Type choice.

Types of app registration

When you register an app with Azure AD one of the decisions you must make is the Application type. There are two types of applications you can register:

Application type Description
Web app /API Web client
A type of client application that executes all code on a web server.

User-agent-based client
A type of client application that downloads code from a web server and executes within a user-agent (for instance, a web browser), such as a Single Page Application (SPA).
Native A type of client application that is installed natively on a device.

When you select Web app /API you must provide a Sign-On URL which is the URL where Azure AD will send the authentication response, including a token if authentication was successful. While you develop an app, this is usually set to http://localhost/appname:[port] so you can develop and debug your app locally. When you publish your app, you need to change this value to the published URL of the app.

When you select Native, you must provide a Redirect URI. This is a unique identifier to which Azure AD will redirect the user-agent in an OAuth 2.0 request. This is typically a value formatted like so: //app:<guid>.

Giving access to Common Data Service

If your app will be a client which allows the authenticated user to perform operations, you must configure the application to have the Access Dynamics 365 as organization users delegated permission.

For specific steps to do this, see Walkthrough: Register an app with Azure Active Directory > Apply Permissions.

If your app will use Server-to-Server (S2S) authentication, this step is not required. That configuration requires a specific system user and the operations will be performed by that user account rather than any user that must be authenticated.

Enable Implicit Flow

If you are configuring an app for a Single Page Application (SPA) you must edit the Manifest to set the oauth2AllowImplicitFlow value to true. More information: Walkthrough: Registering and configuring a SPA application with adal.js

Use Client Secrets & Certificates

For server to server scenarios there will not be an interactive user account to authenticate. In these cases, you need to provide some means to confirm that the application is trusted. This is done using client secrets or certificates.

For apps that are registered with the Web app /API application type, you can configure secrets. These are set using the Keys area under API Access in the Settings for the app registration.

For either application type, you can upload a certificate.

More information: Connect as an app

Use ADAL Libraries to connect

Use one of the Microsoft-supported Azure Active Directory Authentication Libraries (ADAL) client libraries. Azure Active Directory Authentication Libraries > Microsoft-supported Client Libraries.

These libraries are available for various platforms as shown in the following table:

Platform Library
.NET Client, Windows Store, UWP, Xamarin ADAL .NET v3
.NET Client, Windows Store, Windows Phone 8.1 ADAL .NET v2
JavaScript ADAL.js
iOS, macOS ADAL
Android ADAL
Node.js ADAL
Java ADAL4J
Python ADAL

Note

Currently, all our samples use the .NET Client libraries except for Use OAuth with Cross-Origin Resource Sharing to connect a Single Page Application which uses the JavaScript ADAL.js library.

ADAL .NET Client library versions

There are two libraries that support .NET clients. The ADAL .NET v3 library is the latest but does not replace the ADAL .NET v2 library.

Important

If you are using Xrm.Tooling for .NET framework applications, you must use the ADAL .NET v2 library.

One of the significant differences between the .NET Client versions is that the v2 library provides support for passing user credentials. The v3 library requires that user credential information must be captured interactively using a browser pop-up.

If you are not using Xrm.Tooling you may use the ADAL .NET v2 or v3 client libraries with the Web API. For an example using the v3 client library see : ADAL v3 WhoAmI sample.

Use the AccessToken with your requests

The point of using the ADAL libraries is to get a token that you can include with your requests. This only requires a few lines of code, and just a few more lines to configure an HttpClient to execute a request.

Simple example

The following is the minimum amount of code needed to execute a single Web Api request, but it is not the recommended approach:

class SampleProgram
{
    private static string serviceUrl = "https://yourorg.crm.dynamics.com"; 
    private static string clientId = "51f81489-12ee-4a9e-aaae-a2591f45987d"; 
    private static string userName = "you@yourorg.onmicrosoft.com";
    private static string password = "yourpassword";

    static void Main(string[] args)
    {

        AuthenticationContext authContext = 
        new AuthenticationContext("https://login.microsoftonline.com/common", false);  
        UserCredential credential = new UserCredential(userName, password);
        AuthenticationResult result = authContext.AcquireToken(serviceUrl, clientId, credential);
        //The access token
        string accessToken = result.AccessToken;

        using (HttpClient client = new HttpClient()) {
        client.BaseAddress = new Uri(serviceUrl);
        client.Timeout = new TimeSpan(0, 2, 0);  //2 minutes  
        client.DefaultRequestHeaders.Add("OData-MaxVersion", "4.0");
        client.DefaultRequestHeaders.Add("OData-Version", "4.0");
        client.DefaultRequestHeaders.Accept.Add(
            new MediaTypeWithQualityHeaderValue("application/json"));
        HttpRequestMessage request = 
            new HttpRequestMessage(HttpMethod.Get, "/api/data/v9.0/WhoAmI");
        //Set the access token
        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
        HttpResponseMessage response = client.SendAsync(request).Result;
        if (response.IsSuccessStatusCode)
        {
            //Get the response content and parse it.  
            JObject body = JObject.Parse(response.Content.ReadAsStringAsync().Result);
            Guid userId = (Guid)body["UserId"];
            Console.WriteLine("Your system user ID is: {0}", userId);
        }

    }
}

This simple approach does not represent a good pattern to follow because the AccessToken will expire in about an hour. ADAL libraries will cache the token for you and will refresh it each time the AcquireToken method is called.

Example demonstrating a DelegatingHandler

The recommended approach is to implement a class derived from DelegatingHandler which will be passed to the constructor of the HttpClient. This handler will allow you to override the HttpClient.SendAsync method so that ADAL will call the AcquireToken method with each request sent by the http client.

The following is an example of a custom class derived from DelegatingHandler

  /// <summary>  
  ///Custom HTTP message handler that uses OAuth authentication thru ADAL.  
  /// </summary>  
  class OAuthMessageHandler : DelegatingHandler
  {
    private UserCredential _credential;
    private AuthenticationContext _authContext = 
      new AuthenticationContext("https://login.microsoftonline.com/common", false);
    private string _clientId;
    private string _serviceUrl;

    public OAuthMessageHandler(string serviceUrl, string clientId, string userName, string password,
            HttpMessageHandler innerHandler)
        : base(innerHandler)
    {
      _credential = new UserCredential(userName, password);
      _clientId = clientId;
      _serviceUrl = serviceUrl;
    }

    protected override Task<HttpResponseMessage> SendAsync(
             HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
    {
      try
      {
        request.Headers.Authorization =
        new AuthenticationHeaderValue("Bearer", _authContext.AcquireToken(_serviceUrl, _clientId, _credential).AccessToken);
      }
      catch (Exception ex)
      {
        throw ex;
      }      
      return base.SendAsync(request, cancellationToken);
    }
  }

Using this OAuthMessageHandler class, the simple program shown above would look like this, with some additional error handling included:

class SampleProgram
{
    private static string serviceUrl = "https://yourorg.crm.dynamics.com"; 
    private static string clientId = "51f81489-12ee-4a9e-aaae-a2591f45987d"; 
    private static string userName = "you@yourorg.onmicrosoft.com";
    private static string password = "yourpassword";

    static void Main(string[] args)
    {
       HttpMessageHandler messageHandler;

      try
      {
        messageHandler = new OAuthMessageHandler(serviceUrl, clientId, userName, password,
                         new HttpClientHandler());
        //Create an HTTP client to send a request message to the CRM Web service.  
        using (HttpClient client = new HttpClient(messageHandler))
        {
          //Specify the Web API address of the service and the period of time each request   
          // has to execute.  
          client.BaseAddress = new Uri(serviceUrl);
          client.Timeout = new TimeSpan(0, 2, 0);  //2 minutes
          client.DefaultRequestHeaders.Add("OData-MaxVersion", "4.0");
          client.DefaultRequestHeaders.Add("OData-Version", "4.0");
          client.DefaultRequestHeaders.Accept.Add(
              new MediaTypeWithQualityHeaderValue("application/json"));

          //Send the WhoAmI request to the Web API using a GET request.   
          var response = client.GetAsync("api/data/v9.0/WhoAmI",
                  HttpCompletionOption.ResponseHeadersRead).Result;
          if (response.IsSuccessStatusCode)
          {
            //Get the response content and parse it.  
            JObject body = JObject.Parse(response.Content.ReadAsStringAsync().Result);
            Guid userId = (Guid)body["UserId"];
            Console.WriteLine("Your system user ID is: {0}", userId);
          }
          else
          {
            throw new Exception(response.ReasonPhrase);
          }
        }
      }
      catch (Exception ex)
      {
        DisplayException(ex);
      }
    }

    /// <summary> Displays exception information to the console. </summary>  
    /// <param name="ex">The exception to output</param>  
    private static void DisplayException(Exception ex)
    {
      Console.WriteLine("The application terminated with an error.");
      Console.WriteLine(ex.Message);
      while (ex.InnerException != null)
      {
        Console.WriteLine("\t* {0}", ex.InnerException.Message);
        ex = ex.InnerException;
      }
    }
}

Even though this example uses HttpClient.GetAsync rather than the overridden SendAsync, it will apply for any of the HttpClient methods that send a request.

Connect as an app

Some apps you will create are not intended to be run interactively by a user. For example, you may want to make a web client application that can perform operations on Common Data Service data, or a console application that performs a scheduled task of some kind.

While you could achieve these scenarios using credentials for an ordinary user, that user account would need to use a paid license. This isn't the recommended approach.

In these cases you can create a special application user which is bound to an Azure Active Directory registered application and use either a key secret configured for the app or upload a X.509 certificate. Another benefit of this approach is that it doesn't consume a paid license.

Requirements to connect as an app

To connect as an app you will need:

  • A registered app
  • A Common Data Service user bound to the registered app
  • Connect using either the application secret or a certificate thumbprint

Register your app

When registering an app you follow many of the same steps described in Walkthrough: Register an app with Azure Active Directory, with the following exceptions:

  • You do not need to grant the Access Dynamics 365 as organization users permission.

    This application will be bound to a specific user account.

  • You must configure a secret for the app registration OR upload a public key certificate.

While registering the app, select the Keys section on the Settings page.

To add a certificate:

  1. Select Upload Public Key.
  2. Select the file you'd like to upload. It must be one of the following file types: .cer, .pem, .crt.

To add a password:

  1. Add a description for your key.
  2. Select a duration.
  3. Select Save.

The right-most column will contain the key value, after you save the configuration changes. Be sure to copy the key for use in your client application code, as it is not accessible once you leave this page.

Common Data Service user account bound to the registered app

The first thing you must do is create a custom security role that will define what access and privileges this account will have within the Common Data Service organization. More information: Create or configure a custom security role.

After you have created the custom security role, you must create the user account which will use it.

Manually create a Common Data Service application user

The procedure to create this user is different from creating a licensed user. Use the following steps:

  1. Navigate to Settings > Security > Users

  2. In the view drop-down, select Application Users.

  3. Click New. Then verify that you are using the Application user form.

    If you do not see the Application ID, Application ID URI and Azure AD Object ID fields in the form, you must select the Application User form from the list:

    Select Application User Form

  4. Add the appropriate values to the fields:

    Field Value
    User Name A name for the user
    Application ID The Application ID value for the application registered with Azure AD.
    Full Name The name of your application.
    Primary Email The email address for the user.

    The Application ID URI and Azure AD Object ID fields are locked and you cannot set values for these fields.

    When you create this user the values for these fields will be retrieved from Azure AD based on the Application ID value when you save the user.

  5. Associate the application user with the custom security role you created.

Connect using the application secret

If you are connecting using an secret configured for the application, you will use the ClientCredential class passing in the clientId and clientSecret rather than a UserCredential with userName and password parameters.

string serviceUrl = "https://yourorg.crm.dynamics.com";
string clientId = "<your app id>";
string secret = "<your app secret>";

AuthenticationContext authContext = new AuthenticationContext("https://login.microsoftonline.com/common", false);
ClientCredential credential = new ClientCredential(clientId, secret);

AuthenticationResult result = authContext.AcquireToken(serviceUrl, credential);

string accessToken = result.AccessToken;

Connect using a certificate thumbprint

If you are connecting using a certificate and using the Microsoft.Xrm.Tooling.Connector.CrmServiceClient you can use code like the following:

string CertThumbPrintId = "DC6C689022C905EA5F812B51F1574ED10F256FF6";
string AppID = "545ce4df-95a6-4115-ac2f-e8e5546e79af";
string InstanceUri = "https://yourorg.crm.dynamics.com";

string ConnectionStr = $@"AuthType=Certificate;
                        SkipDiscovery=true;url={InstanceUri};
                        thumbprint={CertThumbPrintId};
                        ClientId={AppID};
                        RequireNewInstance=true";
using (CrmServiceClient svc = new CrmServiceClient(ConnectionStr))
{
    if (svc.IsReady)
    {
    //your code goes here
    }

}

See also

Authentication with Common Data Service web services
Authentication with .NET Framework applications