Share via


Web API Helper code: 認証クラス

 

公開日: 2017年1月

対象: Dynamics 365 (online)、Dynamics 365 (on-premises)、Dynamics CRM 2016、Dynamics CRM Online

[Authentication] クラスを使用して、[Microsoft Dynamics 365] Web サービスへの接続の検証を確立することを支援します。 このクラスは、2 つの認証プロトコルをサポートしています:「Windows 認証 Dynamics 365 設置型」または「OAuth 2.0Dynamics 365 (オンライン) またはインターネットに接続する展開 (IFDs)」 このクラスは、OAuth プロトコルを処理するために Microsoft Azure の Active Directory 認証ライブラリ (ADAL) へ依存します。

[Authentication] クラスは、[CRM SDK Web API ヘルパー ライブラリ] の Authentication.cs ファイルにあります。 オブジェクトの種類 System.Net.Http HttpMessageHandler を通じて、Dynamics 365 サービスで保護された接続の確立ができるよう Configuration ヘルパー クラスの階層と組み合わせて動作するように設計されています。 詳細については、「Microsoft Dynamics 365 Web API Helper Library (C#) の使用」を参照してください。

認証プロセス

Dynamics 365 サービスの認証を使用する [Authentication] クラスのメカニズムは、[Configuration] パラメーターを持つコンストラクターが渡す情報によって異なります。 Dynamics 365 サービスでセキュリティ保護された、永続的な通信セッションを提供する System.Net.Http HttpClient インスタンスを作成するために使用できる HttpMessageHandler-派生したオブジェクトを構築しようとします。

まず、指定された Dynamics 365 サービスが OAuth または Windows ネイティブ認証で使用されるかどうかを決定する簡単な検出ハンドシェイクが実行されます。

  • OAuth が使用される場合、[OAuthMessageHandler] オブジェクトは、ハンドシェイクで検出された認証を使用して作成されます。DelegatingHandler から派生したこのクラスでは、すべての要求が OAuth アクセス トークンに更新されるため、トークンの有効期限を明示的に管理する必要はありません。

  • Windows 認証を使用する場合、ユーザーの資格情報を指定すると、これらの資格情報を使用して HttpClientHandler が構築されます。

  • Windows 認証が使用されているがユーザー資格情報が指定されていない場合、HttpClientHandle は既定のネットワーク資格情報を使用して構築されます。

クラスの階層とメンバー

次のテーブルは、[Authentication] クラスのパブリック メンバーを示します。

Dynamics 365 Web API Helper Library-Authentication Class Diagram

認証クラス

プロパティ:

Authority – OAuth 認証を管理しているサーバーの URL です。

ClientHandlerHttpMessageHandler – 派生したオブジェクトは、ネットワークの資格情報またはメッセージ要求に対する認証トークンを提供します。

Context認証コンテキスト 認証のイベント用。


メソッド:

AquireToken OAuth 用は、現在の認証コンテキストの更新とアクセス トークンを含む 認証結果 を返します。

[Authentication] は、[Configuration] パラメーターを使用してクラス インスタンスを初期化します。

[DiscoverAuthority] は、Dynamics 365 Web サービスの認証の権限を検出します。


OAuthMessageHandler クラス

この入れ子になったクラスは、Dynamics 365 (Online)、および IFD 展開の各送信メッセージの認証ヘッダーを設定します。

使用法

[Configuration] および [Authentication] クラスは、Dynamics 365 サービスへのセキュリティ保護された接続の確立と並行して使用するように設計されています。 まず、[Configuration] でオブジェクトの種類を作成し、[Authentication] コンストラクターに単一のパラメーターとして渡します。 正常に作成後、Dynamics 365 サービスに対するセキュリティ保護された、永続的な HTTP クライアント接続を構築するために、[ClientHandler] プロパティを使用することができます。

ほとんどの Web API C# サンプルで使用されるこの操作を実行するための一般的な方法の 1 つは、次の行で示されるように、正常に作成された構成ファイルから接続情報を読み取るための [FileConfiguration] 派生クラスを使用することです。

FileConfiguration config = new FileConfiguration(null);
Authentication auth = new Authentication(config);
httpClient = new HttpClient(auth.ClientHandler, true);

この使用方法の追加詳細については、「FileConfiguration 接続設定」セクションを参照してください。 [Authentication] クラスには、他のいくつかのパブリック プロパティとメソッドが含まれていますが、これらは主に [ClientHandler] プロパティに対する作成のサポートを提供するものであって、ほとんどのクライアント アプリケーションでは直接にアクセスされません。

クラスの一覧

このクラスの最新のソースは、CRM SDK Web API Helper Library NuGet パッケージにあります。

using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security;
using System.Threading.Tasks;

namespace Microsoft.Crm.Sdk.Samples.HelperCode
{
    /// <summary>
    /// Manages user authentication with the Dynamics CRM Web API (OData v4) services. This class uses Microsoft Azure
    /// Active Directory Authentication Library (ADAL) to handle the OAuth 2.0 protocol. 
    /// </summary>
    public class Authentication
    {
        private Configuration _config = null;
        private HttpMessageHandler _clientHandler = null;
        private AuthenticationContext _context = null;
        private string _authority = null;

        #region Constructors
        /// <summary>
        /// Base constructor.
        /// </summary>
        public Authentication() { }

        /// <summary>
        /// Establishes an authentication session for the service.
        /// </summary>
        /// <param name="config">A populated configuration object.</param>
        public Authentication(Configuration config)
            : base()
        {
            if (config == null)
                throw new Exception("Configuration cannot be null.");

            _config = config;

            SetClientHandler();
        }

        /// <summary>
        /// Custom constructor that allows adding an authority determined asynchronously before 
        /// instantiating the Authentication class.
        /// </summary>
        /// <remarks>For a WPF application, first call DiscoverAuthorityAsync(), and then call this
        /// constructor passing in the authority value.</remarks>
        /// <param name="config">A populated configuration object.</param>
        /// <param name="authority">The URL of the authority.</param>
        public Authentication(Configuration config, string authority)
            : base()
        {
            if (config == null)
                throw new Exception("Configuration cannot be null.");

            _config = config;
            Authority = authority;

            SetClientHandler();
        }
        #endregion Constructors

        #region Properties
        /// <summary>
        /// The authentication context.
        /// </summary>
        public AuthenticationContext Context
        {
            get
            { return _context; }

            set
            { _context = value; }
        }

        /// <summary>
        /// The HTTP client message handler.
        /// </summary>
        public HttpMessageHandler ClientHandler
        {
            get
            { return _clientHandler; }

            set
            { _clientHandler = value; }
        }


        /// <summary>
        /// The URL of the authority to be used for authentication.
        /// </summary>
        public string Authority
        {
            get
            {
                if (_authority == null)
                    _authority = DiscoverAuthority(_config.ServiceUrl);

                return _authority;
            }

            set { _authority = value; }
        }
        #endregion Properties

        #region Methods
        /// <summary>
        /// Returns the authentication result for the configured authentication context.
        /// </summary>
        /// <returns>The refreshed access token.</returns>
        /// <remarks>Refresh the access token before every service call to avoid having to manage token expiration.</remarks>
        public AuthenticationResult AcquireToken()
        {
            if (_config != null && (!string.IsNullOrEmpty(_config.Username) && _config.Password != null))
            {
                UserCredential cred = new UserCredential(_config.Username, _config.Password);
                return _context.AcquireToken(_config.ServiceUrl, _config.ClientId, cred);
            }
            return _context.AcquireToken(_config.ServiceUrl, _config.ClientId, new Uri(_config.RedirectUrl),
                PromptBehavior.Auto);
        }

        /// <summary>
        /// Returns the authentication result for the configured authentication context.
        /// </summary>
        /// <param name="username">The username of a CRM system user in the target organization. </param>
        /// <param name="password">The password of a CRM system user in the target organization.</param>
        /// <returns>The authentication result.</returns>
        /// <remarks>Setting the username or password parameters to null results in the user being prompted to
        /// enter log-on credentials. Refresh the access token before every service call to avoid having to manage
        /// token expiration.</remarks>
        public AuthenticationResult AcquireToken(string username, SecureString password)
        {

            try
            {
                if (!string.IsNullOrEmpty(username) && password != null)
                {
                    UserCredential cred = new UserCredential(username, password);
                    return _context.AcquireToken(_config.ServiceUrl, _config.ClientId, cred);
                }
            }
            catch (Exception e)
            {
                throw new Exception("Authentication failed. Verify the configuration values are correct.", e);
            }
            return null;
        }


        /// <summary>
        /// Discover the authentication authority.
        /// </summary>
        /// <returns>The URL of the authentication authority on the specified endpoint address, or an empty string
        /// if the authority cannot be discovered.</returns>
         public static string DiscoverAuthority(string serviceUrl)
        {
            try
            {
                AuthenticationParameters ap = AuthenticationParameters.CreateFromResourceUrlAsync(
                    new Uri(serviceUrl + "api/data/")).Result;

                return ap.Authority;
            }
            catch (HttpRequestException e)
            {
                throw new Exception("An HTTP request exception occurred during authority discovery.", e);
            }
            catch (System.Exception e )
            {
                // This exception ocurrs when the service is not configured for OAuth.
                if( e.HResult == -2146233088 )
                {
                    return String.Empty;
                }
                else
                {
                    throw e;
                }
            }
        }

        /// <summary>
        /// Discover the authentication authority asynchronously.
        /// </summary>
        /// <param name="serviceUrl">The specified endpoint address</param>
        /// <returns>The URL of the authentication authority on the specified endpoint address, or an empty string
        /// if the authority cannot be discovered.</returns>
        public static async Task<string> DiscoverAuthorityAsync(string serviceUrl)
        {
            try
            {
                AuthenticationParameters ap = await AuthenticationParameters.CreateFromResourceUrlAsync(
                    new Uri(serviceUrl + "api/data/"));

                return ap.Authority;
            }
            catch (HttpRequestException e)
            {
                throw new Exception("An HTTP request exception occurred during authority discovery.", e);
            }
            catch (Exception e)
            {
                // These exceptions ocurr when the service is not configured for OAuth.

                // -2147024809 message: Invalid authenticate header format Parameter name: authenticateHeader
                if (e.HResult == -2146233088 || e.HResult == -2147024809)
                {
                    return String.Empty;
                }
                else
                {
                    throw e;
                }
            }
        }

        /// <summary>
        /// Sets the client message handler as appropriate for the type of authentication
        /// in use on the web service endpoint.
        /// </summary>
        private void SetClientHandler()
        {
            // Check the Authority to determine if OAuth authentication is used.
            if (String.IsNullOrEmpty(Authority))
            {
                if (_config.Username != String.Empty)
                {
                    _clientHandler = new HttpClientHandler()
                    { Credentials = new NetworkCredential(_config.Username, _config.Password, _config.Domain) };
                }
                else
                // No username is provided, so try to use the default domain credentials.
                {
                    _clientHandler = new HttpClientHandler()
                    { UseDefaultCredentials = true };
                }
            }
            else
            {
                _clientHandler = new OAuthMessageHandler(this, new HttpClientHandler());
                _context = new AuthenticationContext(Authority, false);
            }
        }
        #endregion Methods

        /// <summary>
        /// Custom HTTP client handler that adds the Authorization header to message requests. This
        /// is required for IFD and Online deployments.
        /// </summary>
        class OAuthMessageHandler : DelegatingHandler
        {
            Authentication _auth = null;

            public OAuthMessageHandler( Authentication auth, HttpMessageHandler innerHandler )
                : base(innerHandler)
            {
                _auth = auth;
            }

            protected override Task<HttpResponseMessage> SendAsync(
                HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
            {
                // It is a best practice to refresh the access token before every message request is sent. Doing so
                // avoids having to check the expiration date/time of the token. This operation is quick.
                request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _auth.AcquireToken().AccessToken);

                return base.SendAsync(request, cancellationToken);
            }
        }
    }
}

関連項目

Microsoft Dynamics 365 Web API (C#) を開始する
Visual Studio (C#) で Dynamics 365 Web API プロジェクトを起動する
Microsoft Dynamics 365 Web API Helper Library (C#) の使用
Web API Helper code: 構成クラス
Web API Helper code: CrmHttpResponseException クラス

Microsoft Dynamics 365

© 2017 Microsoft. All rights reserved. 著作権