Neste guia de instruções, você migrará um aplicativo cliente confidencial da Biblioteca de Autenticação Azure Active Directory para .NET (ADAL.NET) para Biblioteca do Microsoft Authenticator para .NET (MSAL.NET). Aplicativos cliente confidenciais incluem aplicativos Web, APIs Web e aplicativos daemon que chamam outro serviço em seu próprio nome. Para obter mais informações sobre aplicativos confidenciais, consulte fluxos de autenticação e cenários de aplicativo. Se o aplicativo for baseado em ASP.NET Core, consulte Microsoft. Identity.Web.
Para registros de aplicativo:
- Você não precisa criar um novo registro de aplicativo. (Você mantém a mesma ID do cliente.)
- Você não precisa alterar as pré-autenticações (permissões de API consentidas pelo administrador).
Etapas da migração
Localize o código que usa a ADAL.NET em seu aplicativo.
O código que usa ADAL em um aplicativo cliente confidencial instancia AuthenticationContext e chama AcquireTokenByAuthorizationCode ou uma substituição de AcquireTokenAsync com os seguintes parâmetros:
- Uma
resourceId cadeia de caracteres. Essa variável é o URI da ID do aplicativo da API Web que você deseja chamar.
- Uma instância de
IClientAssertionCertificate ou ClientAssertion. Essa instância fornece as credenciais do cliente para seu aplicativo para provar a identidade do seu aplicativo.
Depois de identificar que há aplicativos usando ADAL.NET, instale o pacote NuGet do MSAL.NET Microsoft.Identity.Client e atualize as referências de biblioteca do projeto. Para obter mais informações, consulte Instalar um pacote NuGet. Para usar serializadores de cache de token, instale Microsoft. Identity.Web.TokenCache.
Atualize o código de acordo com o cenário de cliente confidencial. Algumas etapas são comuns e se aplicam em todos os cenários confidenciais do cliente. Outras etapas são exclusivas para cada cenário.
Cenários confidenciais do cliente:
Você pode ter fornecido um wrapper em torno da ADAL.NET para lidar com certificados e cache. Este guia usa a mesma abordagem para ilustrar o processo de migração da ADAL.NET para MSAL.NET. No entanto, esse código é apenas para fins de demonstração. Não copie e cole esses wrappers nem os integre ao seu código tal como estão.
Migrar aplicações de daemon
Os cenários do Daemon usam o fluxo de credencial do cliente OAuth2.0. Eles também são denominados chamadas de serviço a serviço. Seu aplicativo adquire um token em seu próprio nome, não em nome de um usuário.
Descobrir se o código usa cenários de daemon
O código ADAL do seu aplicativo usa cenários de daemon se contiver uma chamada para AuthenticationContext.AcquireTokenAsync com os seguintes parâmetros:
- Um recurso (URI da ID do aplicativo) como um primeiro parâmetro
-
IClientAssertionCertificate ou ClientAssertion como o segundo parâmetro
AuthenticationContext.AcquireTokenAsync não tem um parâmetro do tipo UserAssertion. Em caso afirmativo, seu aplicativo é uma API Web e usa o cenário API Web chamando APIs Web downstream.
Atualizar o código de cenários de daemon
As seguintes etapas para atualizar o código se aplicam a todos os cenários confidenciais do cliente:
- Adicione o namespace MSAL.NET no código-fonte:
using Microsoft.Identity.Client;.
- Em vez de instanciar
AuthenticationContext, use ConfidentialClientApplicationBuilder.Create para instanciar IConfidentialClientApplication.
- Em vez da cadeia de
resourceId caracteres, MSAL.NET usa escopos. Como os aplicativos que usam a ADAL.NET são pré-autorizados, você sempre pode usar os seguintes escopos: new string[] { $"{resourceId}/.default" }.
- Substitua a chamada para
AuthenticationContext.AcquireTokenAsync por uma chamada para IConfidentialClientApplication.AcquireTokenXXX, em que XXX depende do seu cenário.
Nesse 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 daemon:
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;
}
}
Aproveite o cache de tokens
Se você não configurar o armazenamento em cache do token, o emissor do token o limitará, resultando em erros. Também é preciso muito menos para obter um token do cache (10-20ms) do que do ESTS (500-30000ms).
Se você quiser implementar um cache de token distribuído, consulte o cache de token para um aplicativo Web ou API Web (aplicativo cliente confidencial).
Saiba mais sobre o cenário de daemon e como ele é implementado com MSAL.NET ou Microsoft. Identity.Web em novos aplicativos.
Migrar uma API da Web que faz chamadas para APIs da Web de downstream
As APIs Web que chamam APIs Web downstream usam o fluxo OAuth2.0 em nome de (OBO) . A API Web usa o token de acesso recuperado do cabeçalho HTTP Authorize e valida esse token. Esse token é então trocado por outro token para chamar a API Web de back-end. Esse token é usado como uma UserAssertion instância na ADAL.NET e MSAL.NET.
Descubra se seu código usa OBO
O código ADAL do seu aplicativo usa OBO se contiver uma chamada para AuthenticationContext.AcquireTokenAsync com os seguintes parâmetros:
- Um recurso (URI da ID do aplicativo) como um primeiro parâmetro
-
IClientAssertionCertificate ou ClientAssertion como o segundo parâmetro
- Um parâmetro do tipo
UserAssertion
Atualizar o código usando OBO
As seguintes etapas para atualizar o código se aplicam a todos os cenários confidenciais do cliente:
- Adicione o namespace MSAL.NET no código-fonte:
using Microsoft.Identity.Client;.
- Em vez de instanciar
AuthenticationContext, use ConfidentialClientApplicationBuilder.Create para instanciar IConfidentialClientApplication.
- Em vez da cadeia de
resourceId caracteres, MSAL.NET usa escopos. Como os aplicativos que usam a ADAL.NET são pré-autorizados, você sempre pode usar os seguintes escopos: new string[] { $"{resourceId}/.default" }.
- Substitua a chamada para
AuthenticationContext.AcquireTokenAsync por uma chamada para IConfidentialClientApplication.AcquireTokenXXX, em que XXX depende do seu cenário.
Nesse caso, substituímos a chamada para AuthenticationContext.AcquireTokenAsync por uma chamada para IConfidentialClientApplication.AcquireTokenOnBehalfOf.
Aqui está uma comparação do 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;
}
}
Aproveite o cache de tokens
No caso do cache de token em OBOs, use um cache de token distribuído. Para obter detalhes, consulte Cache de token para um aplicativo Web ou API Web (aplicativo cliente confidencial).
app.UseInMemoryTokenCaches(); // or a distributed token cache.
Saiba mais sobre APIs Web que chamam APIs Web downstream e como elas são implementadas com MSAL.NET ou Microsoft.Identity.Web em novos aplicativos.
Migrar um aplicativo Web que chama APIs Web
Se o aplicativo usar ASP.NET Core, recomendamos que você atualize para Microsoft. Identity.Web porque processa tudo para você. Para uma visão geral rápida, consulte o anúncio de disponibilidade geral do Microsoft.Identity.Web. Para obter detalhes sobre como usá-lo em um aplicativo Web, consulte Por que usar Microsoft. Identity.Web em aplicativos Web?.
Aplicativos Web que inserem usuários e chamam APIs Web em nome dos usuários empregam o fluxo de código de autorização do OAuth2.0. Normalmente:
- O aplicativo entra em um usuário executando uma primeira etapa do fluxo de código de autorização acessando a plataforma de identidade da Microsoft para autorizar o ponto de extremidade. O usuário entra e executa autenticações multifator, se necessário. Como resultado dessa operação, o aplicativo recebe o código de autorização. A biblioteca de autenticação não é usada nesta fase.
- O aplicativo executa a segunda etapa do fluxo de código de autorização. Ele usa o código de autorização para obter um token de acesso, um token de ID e um token de atualização. Seu aplicativo precisa fornecer o valor
redirectUri, que é o URI em que o ponto de extremidade da plataforma de identidade da Microsoft fornecerá os tokens de segurança. Depois que o aplicativo recebe esse URI, ele normalmente solicita AcquireTokenByAuthorizationCode que a ADAL ou a MSAL resgatem o código e obtenham um token que será armazenado no cache de token.
- O aplicativo usa ADAL ou MSAL para chamar
AcquireTokenSilent de modo que possa obter tokens para chamar as APIs Web necessárias dos controladores de aplicativos Web.
Descubra se o código usa o fluxo de código de autenticação
O código ADAL para seu aplicativo usará o fluxo de código de autenticação se ele contiver uma chamada para AuthenticationContext.AcquireTokenByAuthorizationCodeAsync.
Atualize o código usando o fluxo de código de autorização
As seguintes etapas para atualizar o código se aplicam a todos os cenários confidenciais do cliente:
- Adicione o namespace MSAL.NET no código-fonte:
using Microsoft.Identity.Client;.
- Em vez de instanciar
AuthenticationContext, use ConfidentialClientApplicationBuilder.Create para instanciar IConfidentialClientApplication.
- Em vez da cadeia de
resourceId caracteres, MSAL.NET usa escopos. Como os aplicativos que usam a ADAL.NET são pré-autorizados, você sempre pode usar os seguintes escopos: new string[] { $"{resourceId}/.default" }.
- Substitua a chamada para
AuthenticationContext.AcquireTokenAsync por uma chamada para IConfidentialClientApplication.AcquireTokenXXX, em que XXX depende do seu cenário.
Nesse 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;
}
}
A chamada AcquireTokenByAuthorizationCode adiciona um token ao cache de token quando o código de autorização é recebido. Para adquirir tokens adicionais para outros recursos ou locatários, use 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;
}
}
Aproveite o cache de tokens
Como seu aplicativo Web usa AcquireTokenByAuthorizationCode, ele precisa usar um cache de token distribuído para o cache de token. Para obter detalhes, consulte Cache de token para um aplicativo Web ou API Web.
app.UseInMemoryTokenCaches(); // or a distributed token cache.
Tratar MsalUiRequiredException
Quando o controlador tenta adquirir um token silenciosamente para diferentes escopos/recursos, a MSAL.NET pode gerar um MsalUiRequiredException conforme o esperado se o usuário precisar entrar novamente ou se o acesso ao recurso exigir mais declarações (devido a uma política de acesso condicional). Para obter detalhes sobre mitigação, veja como lidar com erros e exceções em MSAL.NET.
Saiba mais sobre aplicativos Web chamando APIs Web e como elas são implementadas com MSAL.NET ou Microsoft. Identity.Web em novos aplicativos.
Benefícios da MSAL
Os principais benefícios do MSAL.NET para seu aplicativo incluem:
Resiliência. MSAL.NET ajuda a tornar seu aplicativo resiliente por meio de:
- Benefícios do CCS (Serviço de Credenciais em Cache) do Microsoft Entra ID O CCS funciona como um backup Microsoft Entra.
- Renovação proativa de tokens se a API que você chama permitir tokens de longa duração por meio de avaliação de acesso contínuo.
segurança. Você poderá adquirir tokens PoP (Prova de posse) se a API Web que você deseja chamar exigir isso. Para obter detalhes, consulte tokens de Prova de Posse no MSAL.NET
Desempenho e escalabilidade. Se você não precisar compartilhar seu cache com a ADAL.NET, desabilite a compatibilidade de cache herdada ao criar o aplicativo cliente confidencial (.WithLegacyCacheCompatibility(false)) para aumentar significativamente o desempenho.
app = ConfidentialClientApplicationBuilder.Create(ClientId)
.WithCertificate(certificate)
.WithAuthority(authority)
.WithLegacyCacheCompatibility(false)
.Build();
Troubleshooting
MsalServiceException
As seguintes informações de solução de problemas fazem duas suposições:
- Seu código .NET ADAL estava funcionando.
- Você migrou para a MSAL mantendo a mesma ID do cliente.
Se você receber uma exceção com qualquer 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.
Solucione a exceção usando estas etapas:
- Confirme se você está usando a versão mais recente do MSAL.NET.
- Confirme se o host de autoridade que você definiu ao criar o aplicativo cliente confidencial e o host de autoridade usado com a ADAL são semelhantes. Em particular, é a mesma nuvem (Azure Governamental, Microsoft Azure operado pela 21Vianet ou Azure Alemanha)?
MsalClientException
Em aplicativos multilocatários, especifique uma autoridade comum ao criar o aplicativo para direcionar um locatário específico, como o locatário do usuário, ao chamar uma API Web. Desde o MSAL.NET 4.37.0, quando você especifica .WithAzureRegion ao criar o aplicativo, não é mais possível especificar a autoridade usando .WithAuthority nas solicitações de token. Se você fizer isso, receberá o seguinte erro ao atualizar das 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 corrigir esse problema, substitua .WithAuthority a expressão AcquireTokenXXX por .WithTenantId. Especifique o locatário usando um GUID ou um nome de domínio.
Próximas Etapas
Saiba mais sobre: