Partilhar via


Código auxiliar de API da Web: classe de autenticação

 

Publicado: janeiro de 2017

Aplicável a: Dynamics 365 (online), Dynamics 365 (on-premises), Dynamics CRM 2016, Dynamics CRM Online

Use a classe Authentication para ajudar a estabelecer uma conexão validada com um serviço Web do Microsoft Dynamics 365. Essa classe permite dois protocolos de autenticação: Autenticação do Windows para Dynamics 365 local ou OAuth 2.0 para Dynamics 365 (online) ou implantações para a Internet (IFDs). Essa classe baseia-se na Biblioteca de Autenticação do Active Directory (ADAL) do Microsoft Azure para gerenciar o protocolo OAuth.

A classe Authentication está localizada no arquivo Authentication.cs na Biblioteca Auxiliar da API Web do SDK do CRM. Foi criada para trabalhar em conjunto com a hierarquia de classe auxiliar Configuration para permitir que você estabeleça uma conexão segura com o seu serviço do Dynamics 365 por meio de um objeto de tipo System.Net.Http.HttpMessageHandler. Para obter mais informações, consulte Use a Microsoft Dynamics 365 Biblioteca Auxiliar da API Web (C#).

Processamento de autenticação

O mecanismo que a classe Authentication usa para autenticar com um serviço do Dynamics 365 depende as informações que você passa ao construtor com o parâmetro Configuration. Ele tenta criar um objeto derivado de HttpMessageHandler que você pode usar para instanciar uma instância System.Net.Http.HttpClient para fornecer uma sessão de comunicação segura e duradoura com o serviço do Dynamics 365.

Primeiramente, uma troca de sinais padronizados de descoberta é realizada com o serviço especificado do Dynamics 365 para determinar se o OAuth ou a autenticação nativa do Windows está sendo usada.

  • Se o OAuth for usado, logo um objeto OAuthMessageHandler será criado por meio da autoridade de autenticação que foi descoberta na troca de sinais padronizados. Essa classe, derivada de System.Net.Http.DelegatingHandler, atualiza o token de acesso do OAuth em cada solicitação para que você não precisa controlar explicitamente a expiração do token.

  • Se a autenticação do Windows for usada e as credenciais de usuário forem fornecidas, essas credenciais serão usadas para construir um HttpClientHandler.

  • Se a autenticação do Windows for usada, mas as credenciais de usuário não forem fornecidas, um HttpClientHandler será construído com credenciais de rede padrão.

Hierarquia de classes e membros

A seguinte tabela mostra os membros públicos da classe Authentication.

Biblioteca Auxiliar da API Web do Dynamics 365-Diagrama de Classe de Autenticação

Classe de autenticação

Propriedades:

Authority – a URL do servidor que gerencia a autenticação do OAuth.

ClientHandler – o objeto derivado de HttpMessageHandler que fornece as credenciais de rede ou token de acesso de autorização para solicitações de mensagens.

Context – o AuthenticationContext para um evento de autenticação.


Métodos:

AquireToken – para OAuth, retorna um AuthenticationResult, que contém os tokens de atualização e acesso, para o contexto de autenticação atual.

Authentication – inicializa uma instância dessa classe usando o parâmetro Configuration.

DiscoverAuthority – descobre a autoridade de autenticação do serviço Web do Dynamics 365.


Classe OAuthMessageHandler

Essa classe aninhada define o cabeçalho de autorização de cada mensagem enviada para Dynamics 365 (online) e implantações IFD.

Uso

As classes Configuration e Authentication foram criadas para serem usadas em paralelo para estabelecer uma conexão segura com o serviço do Dynamics 365. Primeiramente, você cria um objeto do tipo Configuration, passando-o depois como o único parâmetro para o construtor Authentication. Após uma criação bem-sucedida, você pode usar a propriedade ClientHandler para construir uma conexão HTTP de cliente segura, autenticada e duradoura com o serviço do Dynamics 365.

Uma forma comum de realizar essa operação, que é usada pela maioria dos exemplos em C# de API Web, é utilizar a classe derivada FileConfiguration para ler as informações de conexão dos arquivos de configuração de aplicativos adequadamente autorizados, conforme demonstrado nas seguintes linhas.

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

Para obter mais informações sobre essa forma de uso, consulte a seção Configurações de conexão de FileConfiguration. Embora a classe Authentication contenha vários outros métodos e propriedades públicos, eles são fornecidos principalmente para permitir a criação da propriedade ClientHandler e raramente seriam acessados diretamente pela maior parte dos aplicativos cliente.

Listagem de classes

A origem mais atual dessa classe se encontra no pacote NuGet Biblioteca Auxiliar da API Web do SDK do CRM.

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

Confira Também

Introdução à API Web do Microsoft Dynamics 365 (C#)
Iniciar um projeto de API Web do Dynamics 365 no Visual Studio (C#)
Use a Microsoft Dynamics 365 Biblioteca Auxiliar da API Web (C#)
Código auxiliar de API da Web: Classes de configuração
Código auxiliar de API da Web: classe CrmHttpResponseException

Microsoft Dynamics 365

© 2017 Microsoft. Todos os direitos reservados. Direitos autorais