OAuth 認証を使用する Microsoft Dataverse

OAuth 2.0 は、認証のための業界標準プロトコルです。 アプリケーション ユーザーが認証のために資格情報を提供すると、 OAuth リソースにアクセスする権限があるかどうかが判断されます。

クライアント アプリケーションは、Web APIを使用してデータにアクセスするために、 OAuth の使用をサポートする必要があります。 OAuth サーバー間アプリケーション シナリオで2要素認証 (2FA) または証明書ベースの認証を有効にします。

OAuth 認証にはIDプロバイダーが必要です。 Dataverse については、Microsoft Entra ID が ID プロバイダーとなります。 Microsoftの職場または学校のアカウントを使用して認証するには、 Microsoft認証ライブラリ (MSAL) を使用します。

注意

この トピック では、認証ライブラリを使用した接続に関連する一般的な概念を紹介します。 Dataverse OAuth このコンテンツでは、開発者が 接続 を実行する方法に重点を置きますが、ライブラリの内部動作については扱いません。 Dataverse OAuth 認証に関する完全な情報については、Microsoft Entra ID ドキュメントを参照してください。 まず、「認証とは何か?」 から始めましょう。

提供するサンプルは適切な登録値で事前に構成されているため、独自のアプリ登録を生成せずに実行できます。 独自のアプリを公開する場合は、独自の登録値を使用する必要があります。

アプリの登録

OAuthを使用して 接続 する場合は、まず Microsoft Entra IDテナントにアプリケーションを登録する必要があります。 アプリを登録する方法は、作成するアプリの種類によって異なります。

いずれの場合も、次の記事で説明されているアプリを登録するための基本的な手順から始めてください: クイック スタート: Microsoft IDプラットフォームにアプリケーションを登録する。 具体的な手順については、「チュートリアル: IDを使用してアプリを登録する」を参照してください。 Dataverse Microsoft Entra

このステップで実行する必要のある決定のほとんどは、アプリケーションの種類の選択に依存します (以下を参照)。

アプリ登録の種類

Microsoft Entra ID にてアプリケーションを登録する際には、アプリケーションの種別を選択する必要があります。 登録できるアプリケーションには 2 種類あります。

アプリケーションの種類 説明
Web アプリ /API ウェブクライアント
すべてのコードをWebサーバー上で実行する クライアント アプリケーション の一種。

ユーザーエージェントベースのクライアント
シングル ページ アプリケーション (SPA) など、Webサーバーからコードをダウンロードし、ユーザー エージェント (Webブラウザーなど) 内で実行する クライアント アプリケーション の一種。
ネイティブ モード デバイスにネイティブにインストールされる クライアント アプリケーション の一種。

選択 Webアプリ /API を使用する場合は、 サインオンURL を指定する必要があります。これは、 Microsoft Entra IDが認証 応答 を送信するURLであり、認証が成功した場合はトークンも含まれます。 アプリを開発している間、このURLは通常 https://localhost/appname:[port] に設定されるため、アプリをローカルで開発およびデバッグできます。 アプリを公開する場合、アプリの公開された URL へのこの値を変更する必要があります。

選択 ネイティブ を使用する場合は、リダイレクトURIを指定する必要があります。 このURLは、 Microsoft Entra IDが OAuth 2.0リクエストでユーザー エージェントをリダイレクトする一意の識別子です。 このURLは通常、 app://<guid> のような形式の値です。

Dataverse へのアクセス権を付与する

アプリが、認証されたユーザーが操作を実行できるクライアントである場合、アクセス許可を委任された組織のユーザーとして Dynamics 365 にアクセスするようアプリケーションを構成する必要があります。

権限を設定するための具体的な手順については、「 IDを使用してアプリを登録する Microsoft Entra 」 を参照してください。

アプリがサーバー間 (S2S) の認証を使用する場合、この手順は必要ありません。 その構成には特定のシステム ユーザーが必要であり、操作は認証が必要な任意のユーザーではなく、そのユーザー アカウントにより実行されます。

クライアント シークレット & 証明書の使用

サーバ間のシナリオでは、認証する対話型ユーザー アカウントはありません。 このような場合、アプリケーションが信頼できることを確認するためのいくつかの手段を提供する必要があります。 これはクライアント シークレットまたは証明書を使用して実行されます。

Webアプリ /API アプリケーション タイプに登録されているアプリの場合は、シークレットを構成できます。 これらは、アプリ登録の 設定APIアクセスキー 領域を使用して設定されます。

どちらのアプリケーションの種類でも、証明書をアップロードできます。

詳細: 接続 アプリとして

認証ライブラリを使用して接続する

マイクロソフトがサポートする Microsoft Entra ID 認証クライアント ライブラリのうちのひとつを使用して Dataverse に接続します (例: Microsoft Authentication Library (MSAL) など)。 提供されたリンクでの説明にあるように、このライブラリはさまざまなプラットフォームで利用できます。

注意

Azure Active Directory 認証ライブラリ (ADAL) は現在アクティブに更新されておらず、2022 年 6 月までのみサポートされる予定です。 MSAL は、プロジェクトで使用するように推奨される認証ライブラリです。

Dataverse での認証に MSAL ライブラリを使用するコードサンプルについては、クイック スタート サンプル を参照してください。

.NET クライアント ライブラリ

Dataverse は OAuth 2.0プロトコルを使用したWeb APIエンドポイント によるアプリケーション認証をサポートします。 カスタム .NET アプリケーションの場合、Web API エンドポイントでのアプリケーション認証に MSAL を使用します。

Dataverse SDK for .NETには、認証を処理するクライアント クラス CrmServiceClientServiceClient が含まれています。 CrmServiceClient クラスは現在、認証にADALを使用していますが、 ServiceClient MSALも使用しています。 これらのクライアントを使用するアプリケーション コードを書き込むと、認証を直接管理する必要がなくなります。 どちらのクライアントも、SDK および Web API エンドポイントで動作します。

要求で AccessToken を使用します

認証ライブラリを使用するポイントは、要求に含めることができるアクセス トークンを取得することです。 トークンを取得するには数行のコードのみが必要で、リクエストを実行するために HttpClient を構成するにはさらに数行必要です。

重要

この記事のサンプル コードに示されているように、パブリック クライアントには "<environment-url>/user_impersonation" スコープを使用します。 機密クライアントの場合は、「<environment-url>/.default」のスコープを使用します。

単純な例

単一の Web API 要求を実行するために必要な最小コード数は次のとおりですが、推奨されている方法ではありません。 このコードはMSALライブラリを使用し、 QuickStart サンプルから取得されていることに注意してください。

string resource = "https://contoso.api.crm.dynamics.com";
var clientId = "51f81489-12ee-4a9e-aaae-a2591f45987d";
var redirectUri = "http://localhost"; // Loopback for the interactive login.

// MSAL authentication
var authBuilder = PublicClientApplicationBuilder.Create(clientId)
    .WithAuthority(AadAuthorityAudience.AzureAdMultipleOrgs)
    .WithRedirectUri(redirectUri)
    .Build();
var scope = resource + "/user_impersonation";
string[] scopes = { scope };

AuthenticationResult token =
    authBuilder.AcquireTokenInteractive(scopes).ExecuteAsync().Result;

// Set up the HTTP client
var client = new HttpClient
{
    BaseAddress = new Uri(resource + "/api/data/v9.2/"),
    Timeout = new TimeSpan(0, 2, 0)  // Standard two minute timeout.
};

HttpRequestHeaders headers = client.DefaultRequestHeaders;
headers.Authorization = new AuthenticationHeaderValue("Bearer", token.AccessToken);
headers.Add("OData-MaxVersion", "4.0");
headers.Add("OData-Version", "4.0");
headers.Accept.Add(
    new MediaTypeWithQualityHeaderValue("application/json"));

// Web API call
var response = client.GetAsync("WhoAmI").Result;

この単純なアプローチは、 token が約1時間で期限切れになるため、追従する にとって適切なパターンとは言えません。 MSALライブラリはトークンをキャッシュし、 AcquireTokenInteractive メソッドが呼び出されるたびにトークンを更新します。 ただし、この単純な例ではトークンを 1 回だけ取得しています。

委任メッセージ ハンドラーを示す例

推奨されるアプローチは、 DelegatingHandler から派生したクラスを実装し、それを HttpClient のコンストラクターに渡すことです。 このハンドラを使用すると、 HttpClient.SendAsync メソッドをオーバーライドして、アクセス トークン が、Httpクライアントから送信される各リクエストで AcquireToken* メソッド呼び出しによって更新されるようになります。

以下は、 DelegatingHandler から派生したカスタム クラスの例です。 このコードは、MSAL認証ライブラリを使用する Enhanced QuickStart サンプルから取得されています。

class OAuthMessageHandler : DelegatingHandler
{
    private AuthenticationHeaderValue authHeader;
    public OAuthMessageHandler(string serviceUrl, string clientId, string redirectUrl, string username, string password,
            HttpMessageHandler innerHandler)
        : base(innerHandler)
    {
        string apiVersion = "9.2";
        string webApiUrl = $"{serviceUrl}/api/data/v{apiVersion}/";
        var authBuilder = PublicClientApplicationBuilder.Create(clientId)
                        .WithAuthority(AadAuthorityAudience.AzureAdMultipleOrgs)
                        .WithRedirectUri(redirectUrl)
                        .Build();
        var scope = serviceUrl + "/user_impersonation";
        string[] scopes = { scope };
        // First try to get an authentication token from the cache using a hint.
        AuthenticationResult authBuilderResult=null;
        try
        {
            authBuilderResult = authBuilder.AcquireTokenSilent(scopes, username)
               .ExecuteAsync().Result;
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine(
                $"Error acquiring auth token from cache:{System.Environment.NewLine}{ex}");
            // Token cache request failed, so request a new token.
            try
            {
                if (username != string.Empty && password != string.Empty)
                {
                    // Request a token based on username/password credentials.
                    authBuilderResult = authBuilder.AcquireTokenByUsernamePassword(scopes, username, password)
                                .ExecuteAsync().Result;
                }
                else
                {
                    // Prompt the user for credentials and get the token.
                    authBuilderResult = authBuilder.AcquireTokenInteractive(scopes)
                                .ExecuteAsync().Result;
                }
            }
            catch (Exception msalex)
            {
                System.Diagnostics.Debug.WriteLine(
                    $"Error acquiring auth token with user credentials:{System.Environment.NewLine}{msalex}");
                throw;
            }
        }
        //Note that an Microsoft Entra ID access token has finite lifetime, default expiration is 60 minutes.
        authHeader = new AuthenticationHeaderValue("Bearer", authBuilderResult.AccessToken);
    }

    protected override Task<HttpResponseMessage> SendAsync(
              HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
    {
        request.Headers.Authorization = authHeader;
        return base.SendAsync(request, cancellationToken);
    }
}

この OAuthMessageHandler クラスを使用すると、単純な Main メソッドは次のようになります。

class Program
{
    static void Main(string[] args)
    {
        try
        {
            //Get configuration data from App.config connectionStrings
            string connectionString = ConfigurationManager.ConnectionStrings["Connect"].ConnectionString;

            using (HttpClient client = SampleHelpers.GetHttpClient(connectionString, SampleHelpers.clientId,
                SampleHelpers.redirectUrl))
            {
                // Use the WhoAmI function
                var response = client.GetAsync("WhoAmI").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 UserId is {0}", userId);
                }
                else
                {
                    Console.WriteLine("The request failed with a status of '{0}'",
                                response.ReasonPhrase);
                }
                Console.WriteLine("Press any key to exit.");
                Console.ReadLine();
            }
        }
        catch (Exception ex)
        {
            SampleHelpers.DisplayException(ex);
            Console.WriteLine("Press any key to exit.");
            Console.ReadLine();
        }
    }
}

構成文字列の値はApp.configファイルの接続文字列に移動され、Httpクライアントは GetHttpClient メソッドで構成されます。

public static HttpClient GetHttpClient(string connectionString, string clientId, string redirectUrl, string version = "v9.2")
{
    string url = GetParameterValueFromConnectionString(connectionString, "Url");
    string username = GetParameterValueFromConnectionString(connectionString, "Username");
    string password = GetParameterValueFromConnectionString(connectionString, "Password");
    try
    {
        HttpMessageHandler messageHandler = new OAuthMessageHandler(url, clientId, redirectUrl, username, password,
                        new HttpClientHandler());

        HttpClient httpClient = new HttpClient(messageHandler)
        {
            BaseAddress = new Uri(string.Format("{0}/api/data/{1}/", url, version)),

            Timeout = new TimeSpan(0, 2, 0)  //2 minutes
        };

        return httpClient;
    }
    catch (Exception)
    {
        throw;
    }
}

完全なコードについては、 Enhanced QuickStart サンプルを参照してください。

この例では、オーバーライドされた HttpClientではなくGetAsync . SendAsyncを使用していますが、リクエストを送信する HttpClient メソッドのいずれにも適用されます。

アプリとして接続

作成する一部のアプリは、ユーザーが対話的に実行するためのものではありません。 たとえば、Dataverse データに対する操作を実行できる Web クライアント アプリケーションや、あるスケジュールに基づいたタスクを実行するコンソールアプリケーションを作成するなどのケースが考えられます。

一般のユーザーの資格情報を使用してこれらのシナリオを実行することはできますが、そのユーザー アカウントでは有料ライセンスを使用する必要があります。 これは推奨されている方法ではありません。

このような場合、登録済みのアプリケーションの Microsoft Entra ID に紐づけられた特別なアプリケーション ユーザーを作成し、アプリケーション用に構成されたキーシークレットを使用するか、X.509 証明書をアップロードすることができます。 この方法の別の利点は、有料ライセンスを使用しないことです。

アプリとして接続するための要求

アプリとして接続するには以下の点が必要です。

  • 登録済みアプリ
  • 登録されたアプリにバインドされた Dataverse ユーザー
  • アプリケーション シークレットまたは証明書サムプリントのいずれかを使用して接続する

アプリの登録

アプリを登録する際には、ウォークスルー: Microsoft Entra ID を使用してアプリを登録する で説明されている手順の多くに従いますが、次の例外があります。

  • 組織ユーザーとしてDynamics 365にアクセスする 権限を付与する必要はありません。

    このアプリケーションは特定のユーザー アカウントにバインドされます。

  • アプリ登録のシークレットを構成するか、公開キー証明書をアップロードする必要があります。

アプリ登録の「 証明書とシークレットの管理>」で資格情報を作成または表示できます

証明書(公開鍵)を追加するには:

  1. [証明書] タブで、選択 [証明書をアップロード]します。
  2. アップロードするファイルを選択します。 次のいずれかのファイルの種類である必要があります: .cer、.pem、.crt。
  3. 説明を入力します。
  4. 追加を選択します。

クライアント シークレット (アプリケーション パスワード) を追加するには:

  1. クライアント シークレット タブで、クライアント シークレットの説明を追加します。
  2. 選択 有効期限。
  3. 追加を選択します。

重要

設定の変更を保存すると、秘密の値が表示されます。 ページを離れるとその値にアクセスできなくなるため、クライアント アプリケーション コードで使用するためにシークレット値を必ずコピーしてください。

詳細情報: 資格情報の追加

登録されたアプリにバインドされた Dataverse のユーザー アカウント

最初に必須となる作業は、セキュリティ役割のカスタマイズです。ここで Dataverse の内部でこのアカウントにどのようなアクセス権や権限を付与するかを定義します。 詳細情報: カスタム セキュリティ ロール を作成または構成する

ユーザー定義セキュリティ ロールを作成した後、使用するユーザー アカウントを作成する必要があります。

Dataverse アプリケーション ユーザーを手動で作成します

アプリケーション ユーザーを作成する手順については、管理 Power Platform の記事「 アプリケーション ユーザーの作成」を参照してください。

アプリケーション ユーザーを作成したら、作成したカスタム セキュリティ ロール にアプリケーション ユーザーを関連付けます。

アプリケーション シークレットを使用して接続する

クライアント シークレットを使用して接続し、 Microsoft.Xrm.Tooling.Connector.CrmServiceClient を使用する場合は、次のようなコードを使用できます。

string SecretID = "00000000-0000-0000-0000-000000000000";
string AppID = "545ce4df-95a6-4115-ac2f-e8e5546e79af";
string InstanceUri = "https://yourorg.crm.dynamics.com";

string ConnectionStr = $@"AuthType=ClientSecret;
                        SkipDiscovery=true;url={InstanceUri};
                        Secret={SecretID};
                        ClientId={AppID};
                        RequireNewInstance=true";
using (ServiceClient svc = new ServiceClient(ConnectionStr))
{
    if (svc.IsReady)
    {
    //your code goes here
    }

}

証明書サムプリントを使用して接続する

証明書を使用して接続し、 Microsoft.Xrm.Tooling.Connector.CrmServiceClient を使用する場合は、次のようなコードを使用できます。

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 (ServiceClient svc = new ServiceClient(ConnectionStr))
{
    if (svc.IsReady)
    {
    //your code goes here
    }

}

関連項目

Webサービスによる認証 Microsoft Dataverse
追従する アプリケーションの認証
Microsoft認証ライブラリの概要