Neste guia prático, irá migrar uma aplicação cliente confidencial do Azure Active Directory Authentication Library for .NET (ADAL.NET) para a Biblioteca de Autenticação da Microsoft for .NET (MSAL.NET). Aplicações cliente confidenciais incluem aplicações web, APIs web e aplicações daemon que chamam outro serviço em seu próprio nome. Para mais informações sobre aplicações confidenciais, consulte Fluxos de autenticação e cenários de aplicação. Se a sua aplicação for baseada no ASP.NET Core, veja Microsoft. Identidade.Web.
Para registos de aplicações:
- Não precisa de criar um novo registo de aplicação. (Mantém o mesmo ID de cliente.)
- Não precisas de alterar as pré-autorizações (permissões da API consentidas pelo administrador).
Etapas de migração
Encontra o código que usa ADAL.NET na tua aplicação.
O código que utiliza o ADAL numa aplicação cliente confidencial instancia AuthenticationContext e invoca AcquireTokenByAuthorizationCode ou uma das substituições de AcquireTokenAsync com os seguintes parâmetros:
- Uma
resourceId cadeia de caracteres. Esta variável é o URI do ID da aplicação da API web que pretende chamar.
- Uma instância de
IClientAssertionCertificate ou ClientAssertion. Esta instância fornece as credenciais do cliente para a sua aplicação para provar a identidade da sua aplicação.
Depois de identificar que tem aplicações que usam ADAL.NET, instale o pacote MSAL.NET NuGet Microsoft. Identity.Client e atualize as referências da sua biblioteca de projetos. Para mais informações, consulte Instalar um pacote NuGet. Para usar serializadores de cache de tokens, instale Microsoft.Identity.Web.TokenCache.
Atualize o código de acordo com o cenário confidencial do cliente. Alguns passos são comuns e aplicam-se a todos os cenários confidenciais do cliente. Outros passos são únicos para cada cenário.
Cenários confidenciais para clientes:
Podes ter fornecido um wrapper em ADAL.NET para gerir certificados e cache. Este guia utiliza a mesma abordagem para ilustrar o processo de migração do ADAL.NET para o MSAL.NET. No entanto, este código é apenas para fins de demonstração. Não copies ou coles estes wrappers nem os integres no teu código tal como estão.
Migrar aplicações daemon
Cenários de daemon utilizam o fluxo de credenciais do cliente OAuth2.0. Também são chamadas de chamadas entre serviços. A sua aplicação adquire um token em nome próprio, não em nome do utilizador.
Descobre se o teu código usa cenários de demónio
O código ADAL da sua aplicação utiliza cenários de daemon se contiver uma chamada a AuthenticationContext.AcquireTokenAsync com os seguintes parâmetros:
- Um recurso (ID da aplicação URI) como primeiro parâmetro
-
IClientAssertionCertificate ou ClientAssertion como segundo parâmetro
AuthenticationContext.AcquireTokenAsync não tem um parâmetro do tipo UserAssertion. Se for esse o caso, então a sua aplicação é uma API Web e utiliza o cenário API Web que chama APIs Web a jusante.
Atualizar o código dos cenários de demónios
Os seguintes passos para atualizar o código aplicam-se a todos os cenários confidenciais do cliente:
- Adicione o espaço de nomes MSAL.NET ao seu código-fonte:
using Microsoft.Identity.Client;.
- Em vez de instanciar
AuthenticationContext, use ConfidentialClientApplicationBuilder.Create para instanciar IConfidentialClientApplication.
- Em vez da cadeia de caracteres
resourceId, o MSAL.NET utiliza âmbitos. Como as aplicações que utilizam ADAL.NET são pré-autorizadas, pode sempre usar os seguintes âmbitos: new string[] { $"{resourceId}/.default" }.
- Substitua a chamada para
AuthenticationContext.AcquireTokenAsync por uma chamada para IConfidentialClientApplication.AcquireTokenXXX, onde XXX depende do seu cenário.
Neste caso, substitua a chamada para AuthenticationContext.AcquireTokenAsync por uma chamada para IConfidentialClientApplication.AcquireTokenClient.
Aqui está uma comparação do código ADAL.NET e MSAL.NET para cenários de daemons:
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
public partial class AuthWrapper
{
const string ClientId = "Guid (AppID)";
const string authority
= "https://login.microsoftonline.com/{tenant}";
// App ID URI of web API to call
const string resourceId = "https://target-api.domain.com";
X509Certificate2 certificate = LoadCertificate();
public async Task<AuthenticationResult> GetAuthenticationResult()
{
var authContext = new AuthenticationContext(authority);
var clientAssertionCert = new ClientAssertionCertificate(
ClientId,
certificate);
var authResult = await authContext.AcquireTokenAsync(
resourceId,
clientAssertionCert,
);
return authResult;
}
}
using Microsoft.Identity.Client;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
public partial class AuthWrapper
{
const string ClientId = "Guid (Application ID)";
const string authority
= "https://login.microsoftonline.com/{tenant}";
// App ID URI of web API to call
const string resourceId = "https://target-api.domain.com";
X509Certificate2 certificate = LoadCertificate();
IConfidentialClientApplication app;
public async Task<AuthenticationResult> GetAuthenticationResult()
{
var app = ConfidentialClientApplicationBuilder.Create(ClientId)
.WithCertificate(certificate)
.WithAuthority(authority)
.Build();
// Setup token caching https://learn.microsoft.com/azure/active-directory/develop/msal-net-token-cache-serialization?tabs=aspnet
// For example, for an in-memory cache with 1GB limit, use
app.AddInMemoryTokenCache(services =>
{
// Configure the memory cache options
services.Configure<MemoryCacheOptions>(options =>
{
options.SizeLimit = 1024 * 1024 * 1024; // in bytes (1 GB of memory)
});
}
var authResult = await app.AcquireTokenForClient(
new [] { $"{resourceId}/.default" })
// .WithTenantId(specificTenant)
// See https://aka.ms/msal.net/withTenantId
.ExecuteAsync()
.ConfigureAwait(false);
return authResult;
}
}
Tire partido do armazenamento em cache de tokens
Se não configurares a cache do token, o emissor do token vai limitar-te, resultando em erros. Também é muito mais rápido obter um token da cache (10-20 ms) do que obtê-lo a partir do ESTS (500-30000 ms).
Se quiser implementar uma cache de token distribuída, veja Cache de token para uma aplicação web ou API web (aplicação cliente confidencial).
Saiba mais sobre o cenário dos daemons e como é implementado com MSAL.NET ou Microsoft. Identity.Web em novas aplicações.
Migre uma API web que chame APIs web a jusante
As APIs Web que chamam APIs Web subsequentes utilizam o fluxo OAuth2.0 on-behalf-of (OBO). A API web utiliza o token de acesso recuperado do cabeçalho HTTP Authorize e valida este token. Este token é depois trocado por outro token para chamar a API Web subsequente. Este token é usado como UserAssertion instância tanto no ADAL.NET como no MSAL.NET.
Descobre se o teu código usa OBO
O código ADAL da sua aplicação usa OBO se contiver uma chamada a AuthenticationContext.AcquireTokenAsync com os seguintes parâmetros:
- Um recurso (ID da aplicação URI) como primeiro parâmetro
-
IClientAssertionCertificate ou ClientAssertion como segundo parâmetro
- Um parâmetro de tipo
UserAssertion
Atualize o código usando o OBO
Os seguintes passos para atualizar o código aplicam-se a todos os cenários confidenciais do cliente:
- Adicione o espaço de nomes MSAL.NET ao seu código-fonte:
using Microsoft.Identity.Client;.
- Em vez de instanciar
AuthenticationContext, use ConfidentialClientApplicationBuilder.Create para instanciar IConfidentialClientApplication.
- Em vez da cadeia de caracteres
resourceId, o MSAL.NET utiliza âmbitos. Como as aplicações que utilizam ADAL.NET são pré-autorizadas, pode sempre usar os seguintes âmbitos: new string[] { $"{resourceId}/.default" }.
- Substitua a chamada para
AuthenticationContext.AcquireTokenAsync por uma chamada para IConfidentialClientApplication.AcquireTokenXXX, onde XXX depende do seu cenário.
Neste caso, substituímos a chamada para AuthenticationContext.AcquireTokenAsync por uma chamada para IConfidentialClientApplication.AcquireTokenOnBehalfOf.
Aqui está uma comparação de código OBO de exemplo para ADAL.NET e MSAL.NET:
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
public partial class AuthWrapper
{
const string ClientId = "Guid (AppID)";
const string authority
= "https://login.microsoftonline.com/common";
X509Certificate2 certificate = LoadCertificate();
public async Task<AuthenticationResult> GetAuthenticationResult(
string resourceId,
string tokenUsedToCallTheWebApi)
{
var authContext = new AuthenticationContext(authority);
var clientAssertionCert = new ClientAssertionCertificate(
ClientId,
certificate);
var userAssertion = new UserAssertion(tokenUsedToCallTheWebApi);
var authResult = await authContext.AcquireTokenAsync(
resourceId,
clientAssertionCert,
userAssertion,
);
return authResult;
}
}
using Microsoft.Identity.Client;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
public partial class AuthWrapper
{
const string ClientId = "Guid (Application ID)";
const string authority
= "https://login.microsoftonline.com/common";
X509Certificate2 certificate = LoadCertificate();
IConfidentialClientApplication app;
public async Task<AuthenticationResult> GetAuthenticationResult(
string resourceId,
string tokenUsedToCallTheWebApi)
{
var app = ConfidentialClientApplicationBuilder.Create(ClientId)
.WithCertificate(certificate)
.WithAuthority(authority)
.Build();
// Setup token caching https://learn.microsoft.com/azure/active-directory/develop/msal-net-token-cache-serialization?tabs=aspnet
// For example, for an in-memory cache with 1GB limit. For OBO, it is recommended to use a distributed cache like Redis.
app.AddInMemoryTokenCache(services =>
{
// Configure the memory cache options
services.Configure<MemoryCacheOptions>(options =>
{
options.SizeLimit = 1024 * 1024 * 1024; // in bytes (1 GB of memory)
});
}
var userAssertion = new UserAssertion(tokenUsedToCallTheWebApi);
var authResult = await app.AcquireTokenOnBehalfOf(
new string[] { $"{resourceId}/.default" },
userAssertion)
// .WithTenantId(specificTenant)
// See https://aka.ms/msal.net/withTenantId
.ExecuteAsync()
.ConfigureAwait(false);
return authResult;
}
}
Tire partido do armazenamento em cache de tokens
Para o armazenamento em cache de tokens em OBOs, utilize uma cache distribuída. Para mais detalhes, consulte Cache de tokens para uma aplicação web ou API web (aplicação cliente confidencial).
app.UseInMemoryTokenCaches(); // or a distributed token cache.
Saiba mais sobre APIs Web que chamam outras APIs Web subsequentes e sobre como são implementadas com o MSAL.NET ou o Microsoft.Identity.Web em novas aplicações.
Migre uma aplicação web que chame APIs web
Se a sua aplicação usa ASP.NET Core, recomendamos vivamente que atualize para a Microsoft. Identity.Web porque processa tudo por si. Para uma apresentação rápida, consulte o anúncio de disponibilidade geral do Microsoft.Identity.Web. Para detalhes sobre como usá-lo numa aplicação web, veja Porque usar a Microsoft. Identity.Web em aplicações web?.
As aplicações web que fazem login com utilizadores e contactam APIs web em nome destes utilizam o fluxo de código de autorização OAuth2.0. Tipicamente:
- A aplicação inicia a sessão de um utilizador ao executar a primeira etapa do fluxo do código de autorização, acedendo ao ponto final de autorização da plataforma de identidades da Microsoft. O utilizador inicia sessão e realiza autenticações multifator, se necessário. Como resultado desta operação, a aplicação recebe o código de autorização. A biblioteca de autenticação não é usada nesta fase.
- A aplicação executa a segunda etapa do fluxo de código de autorização. Utiliza o código de autorização para obter um token de acesso, um token ID e um token de atualização. A sua aplicação precisa de fornecer o valor
redirectUri, que é o URI em que o endpoint da plataforma de identidade da Microsoft fornece os tokens de segurança. Depois de a aplicação receber esse URI, normalmente chama AcquireTokenByAuthorizationCode para ADAL ou MSAL para trocar o código e obter um token que será armazenado na cache de tokens.
- A aplicação utiliza ADAL ou MSAL para chamar
AcquireTokenSilent e obter tokens para chamar as APIs Web necessárias nos controladores da aplicação Web.
Descobre se o teu código utiliza o fluxo de código de autenticação
O código ADAL da sua aplicação utiliza o fluxo de código de autenticação se contiver uma chamada para AuthenticationContext.AcquireTokenByAuthorizationCodeAsync.
Atualize o código utilizando o fluxo de código de autorização
Os seguintes passos para atualizar o código aplicam-se a todos os cenários confidenciais do cliente:
- Adicione o espaço de nomes MSAL.NET ao seu código-fonte:
using Microsoft.Identity.Client;.
- Em vez de instanciar
AuthenticationContext, use ConfidentialClientApplicationBuilder.Create para instanciar IConfidentialClientApplication.
- Em vez da cadeia de caracteres
resourceId, o MSAL.NET utiliza âmbitos. Como as aplicações que utilizam ADAL.NET são pré-autorizadas, pode sempre usar os seguintes âmbitos: new string[] { $"{resourceId}/.default" }.
- Substitua a chamada para
AuthenticationContext.AcquireTokenAsync por uma chamada para IConfidentialClientApplication.AcquireTokenXXX, onde XXX depende do seu cenário.
Neste caso, substitua a chamada para AuthenticationContext.AcquireTokenAsync por uma chamada para IConfidentialClientApplication.AcquireTokenByAuthorizationCode.
Aqui está uma comparação dos fluxos de código de autorização de exemplo para ADAL.NET e MSAL.NET:
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
public partial class AuthWrapper
{
const string ClientId = "Guid (AppID)";
const string authority
= "https://login.microsoftonline.com/common";
private Uri redirectUri = new Uri("host/login_oidc");
X509Certificate2 certificate = LoadCertificate();
public async Task<AuthenticationResult> GetAuthenticationResult(
string resourceId,
string authorizationCode)
{
var ac = new AuthenticationContext(authority);
var clientAssertionCert = new ClientAssertionCertificate(
ClientId,
certificate);
var authResult = await ac.AcquireTokenByAuthorizationCodeAsync(
authorizationCode,
redirectUri,
clientAssertionCert,
resourceId,
);
return authResult;
}
}
using Microsoft.Identity.Client;
using Microsoft.Identity.Web;
using System;
using System.Security.Claims;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
public partial class AuthWrapper
{
const string ClientId = "Guid (Application ID)";
const string authority
= "https://login.microsoftonline.com/{tenant}";
private Uri redirectUri = new Uri("host/login_oidc");
X509Certificate2 certificate = LoadCertificate();
public IConfidentialClientApplication CreateApplication()
{
IConfidentialClientApplication app;
app = ConfidentialClientApplicationBuilder.Create(ClientId)
.WithCertificate(certificate)
.WithAuthority(authority)
.WithRedirectUri(redirectUri.ToString())
.WithLegacyCacheCompatibility(false)
.Build();
// Add a token cache. For details about other serialization
// see https://aka.ms/msal-net-cca-token-cache-serialization
app.AddInMemoryTokenCache();
return app;
}
// Called from 'code received event'.
public async Task<AuthenticationResult> GetAuthenticationResult(
string resourceId,
string authorizationCode)
{
IConfidentialClientApplication app = CreateApplication();
var authResult = await app.AcquireTokenByAuthorizationCode(
new[] { $"{resourceId}/.default" },
authorizationCode)
.ExecuteAsync()
.ConfigureAwait(false);
return authResult;
}
}
Chamar AcquireTokenByAuthorizationCode adiciona um token à cache de tokens quando o código de autorização é recebido. Para obter tokens adicionais para outros recursos ou locatários, utilize AcquireTokenSilent nos seus controladores.
public partial class AuthWrapper
{
// Called from controllers
public async Task<AuthenticationResult> GetAuthenticationResult(
string resourceId2,
string authority)
{
IConfidentialClientApplication app = CreateApplication();
AuthenticationResult authResult;
var scopes = new[] { $"{resourceId2}/.default" };
var account = await app.GetAccountAsync(ClaimsPrincipal.Current.GetMsalAccountId());
try
{
// try to get an already cached token
authResult = await app.AcquireTokenSilent(
scopes,
account)
// .WithTenantId(specificTenantId)
// See https://aka.ms/msal.net/withTenantId
.ExecuteAsync().ConfigureAwait(false);
}
catch (MsalUiRequiredException)
{
// The controller will need to challenge the user
// including asking for claims={ex.Claims}
throw;
}
return authResult;
}
}
Tire partido do armazenamento em cache de tokens
Como a tua aplicação web usa AcquireTokenByAuthorizationCode, precisa de usar uma cache de token distribuída para cache de tokens. Para mais detalhes, consulte Cache de tokens para uma aplicação web ou API web.
app.UseInMemoryTokenCaches(); // or a distributed token cache.
Tratamento de MsalUiRequiredException
Quando o seu controlador tenta adquirir um token silenciosamente para diferentes escopos/recursos, o MSAL.NET pode lançar um MsalUiRequiredException como esperado se o utilizador precisar de voltar a iniciar sessão, ou se o acesso ao recurso exigir mais reivindicações (devido a uma política de Acesso Condicional). Para detalhes sobre mitigação, veja como lidar com erros e exceções no MSAL.NET.
Saiba mais sobre aplicações web para chamadas de APIs web e como são implementadas com MSAL.NET ou Microsoft. Identity.Web em novas aplicações.
Benefícios do MSAL
Os principais benefícios do MSAL.NET para a sua aplicação incluem:
Resiliência. O MSAL.NET ajuda a tornar a sua aplicação resiliente através de:
- Benefícios do Serviço de Credenciais em Cache (CCS) do Microsoft Entra ID. O CCS funciona como backup da Microsoft Entra.
- Renovação proativa de tokens se a API que invoca suportar tokens de longa duração através de avaliação contínua de acesso.
Security. Pode adquirir tokens de Prova de Posse (PoP) se a API web que pretende chamar o exigir. Para obter mais detalhes, consulte tokens de prova de posse no MSAL.NET
Desempenho e escalabilidade. Se não precisares de partilhar a cache com ADAL.NET, desativa a compatibilidade de cache legada ao criar a aplicação cliente confidencial (.WithLegacyCacheCompatibility(false)) para aumentar significativamente o desempenho.
app = ConfidentialClientApplicationBuilder.Create(ClientId)
.WithCertificate(certificate)
.WithAuthority(authority)
.WithLegacyCacheCompatibility(false)
.Build();
Troubleshooting
MsalServiceException
A seguinte informação de resolução de problemas faz duas suposições:
- O teu código ADAL.NET estava a funcionar.
- Migraste para MSAL mantendo o mesmo ID de cliente.
Se obtiver uma exceção com uma das seguintes mensagens:
AADSTS700027: Client assertion contains an invalid signature. [Reason - The key was not found.]
AADSTS90002: Tenant 'aaaabbbb-0000-cccc-1111-dddd2222eeee' not found. This may happen if there are no active
subscriptions for the tenant. Check to make sure you have the correct tenant ID. Check with your subscription
administrator.
Resolva a exceção usando estes passos:
- Confirme que está a usar a versão mais recente do MSAL.NET.
- Confirme que o host de autoridade que definiu ao construir a aplicação cliente confidencial e o host de autoridade que usou com o ADAL são semelhantes. Em particular, trata-se da mesma nuvem (Azure Government, Microsoft Azure operado pela 21Vianet ou Azure Alemanha)?
MsalClientException
Em aplicações multitenant, especifique uma autoridade comum ao criar a aplicação para visar um inquilino específico, como o inquilino do utilizador ao chamar uma API da Web. Desde a versão 4.37.0 do MSAL.NET, quando especifica .WithAzureRegion ao criar a aplicação, já não pode especificar a autoridade utilizando .WithAuthority durante os pedidos de token. Se o fizer, receberá o seguinte erro ao atualizar a partir de versões anteriores do MSAL.NET:
MsalClientException - "You configured WithAuthority at the request level, and also WithAzureRegion. This is not supported when the environment changes from application to request. Use WithTenantId at the request level instead."
Para remediar este problema, substitua .WithAuthority na expressão AcquireTokenXXX por .WithTenantId. Especifique o inquilino usando um GUID ou um nome de domínio.
Passos seguintes
Saiba mais sobre: