Microsoft Dataverse で OAuth 認証を使用する

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

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

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

注意

本トピックでは、OAuth と認証ライブラリを使用した Dataverse への接続に関する共通した概念を扱います。 このコンテンツでは開発者が Dataverse に接続する方法を重点的に扱っており、OAuth やライブラリの内部の動作には焦点をあてていません。 認証に関する完全な情報については、Microsoft Entra ID ドキュメントを参照してください。 認証とは何か のページから参照することをお勧めします。

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

アプリの登録

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

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

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

アプリ登録の種類

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

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

ユーザー エージェント ベースのクライアント
Single Page Application (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 にアクセスするようアプリケーションを構成する必要があります。

アクセス許可を設定する具体的な手順については、Microsoft Entra ID でアプリを登録するを参照してください。

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

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

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

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

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

詳細: アプリとして接続

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

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

注意

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

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

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

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

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

要求で AccessToken を使用します

認証ライブラリを使用するポイントは、要求に含めることができるアクセス トークンを取得することです。 トークンを取得するのに必要なコードはわずか数行で、HttpClient がリクエストを実行するよう構成するには、さらに数行を追加するだけです。

重要

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

単純な例

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

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 回だけ取得しています。

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

推奨されている方法は、HttpClient のコンストラクタに渡される DelegatingHandler から派生したクラスを実装することです。 このハンドラーでは HttpClient を上書きすることができるようになります。SendAsync メソッドによって、HTTP クライアントが要求を送信するたびに AcquireToken* メソッドの呼び出しでアクセス トークンが更新されます。

DelegatingHandler から派生したカスタム クラスの例を以下に示します。 このコードは、MSAL 認証ライブラリを使用する 拡張クイック スタート のサンプルから取得したものです。

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;
    }
}

完全なコードは 拡張クイックスタート のサンプルを参照してください。

その場合この例では、HttpClient を使用します。GetAsync SendAsync を上書きするのではなく、要求に送信される HttpClient メソッドのいずれかを適用します。

アプリとして接続

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

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

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

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

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

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

アプリの登録

アプリを登録する際は、ウォークスルー: Microsoft Entra ID でアプリを登録する に記載されて手順と以下の例外処理に従ってください:

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

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

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

アプリを登録している間に、設定ページのキーセクションを選択します。

証明書を追加するには:

  1. 公開キーのアップロードを選択します。
  2. アップロードするファイルを選択します。 次のいずれかのファイルの種類である必要があります: .cer、.pem、.crt。

パスワードを追加するには:

  1. キーに関する説明を追加します。
  2. 期間を選択します。
  3. 保存を選びます。

構成の変更を保存した後、一番右の列にキー値が表示されます。 このページから一旦離れるとアクセスできないため、クライアント アプリケーション コードで使用するためにキーをコピーしておいてください。

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

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

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

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

このユーザーを作成する手順は、ライセンスを受けたユーザーを作成する手順とは異なります。 次の手順を実行します。

  1. 設定 > セキュリティ > ユーザーの順に移動します

  2. ビュー ドロップダウンで、アプリケーション ユーザーを選択します。

  3. 新規をクリックします。 次にアプリケーション ユーザーフォームを使用していることを確認します。

    フォーム上に アプリケーション ID, アプリケーション ID URIAzure AD オブジェクト ID フィールドがない場合は、リストから アプリケーション ユーザー を選択してください:

    アプリケーション ユーザー フォームの選択。

  4. フィールドに適切な値を追加します:

    フィールド
    ユーザー名 ユーザーの名前
    アプリケーション ID Microsoft Entra ID に登録されたアプリケーションのアプリケーションID の値
    正式な名前 アプリケーションの名前。
    電子メール 1 ユーザーの電子メール アドレス。

    アプリケーション ID URI および Azure AD オブジェクト ID フィールドはロックされており、これらのフィールドに入力することはできません。

    このユーザーを作成すると、これらフィールドの値には当該ユーザーを保存した際の アプリケーションID 値に基づいて Microsoft Entra ID から取得されます。

  5. アプリケーション ユーザーを作成したユーザー定義セキュリティ ロールに関連付けます。

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

クライアント シークレットを使用し 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
    }

}

関連項目

Microsoft Dataverse ウェブサービス を使用した認証
.NET Framework アプリケーションを認証する
Microsoft 認証ライブラリの概要

注意

ドキュメントの言語設定についてお聞かせください。 簡単な調査を行います。 (この調査は英語です)

この調査には約 7 分かかります。 個人データは収集されません (プライバシー ステートメント)。