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.
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.
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