En esta guía práctica, aprenderá a migrar una aplicación cliente confidencial de Azure Active Directory Authentication Library para .NET (ADAL.NET) a Biblioteca de autenticación de Microsoft para .NET (MSAL.NET). Las aplicaciones cliente confidenciales incluyen aplicaciones web, las API web y las aplicaciones daemon que invocan otro servicio en nombre propio. Para más información sobre las aplicaciones confidenciales, consulte Flujos de autenticación y escenarios de aplicaciones. Si la aplicación se basa en ASP.NET Core, consulta Microsoft. Identity.Web.
Para los registros de aplicaciones:
- No es necesario crear un nuevo registro de aplicaciones. (Mantiene el mismo identificador de cliente).
- No es necesario cambiar las autenticaciones previas (permisos de API con consentimiento del administrador).
Pasos de migración
Busque el código que usa ADAL.NET en la aplicación.
El código que usa ADAL en una aplicación cliente confidencial crea instancias de AuthenticationContext y llama a AcquireTokenByAuthorizationCode o a una invalidación de AcquireTokenAsync con los parámetros siguientes:
- Una cadena
resourceId. Esta variable es el URI de identificador de aplicación de la API web a la que quiere llamar.
- Una instancia de
IClientAssertionCertificate o ClientAssertion. Esta instancia proporciona las credenciales de cliente para que la aplicación demuestre la identidad de la aplicación.
Una vez que haya identificado que tiene aplicaciones que usan ADAL.NET, instale el paquete NuGet de MSAL.NET Microsoft.Identity.Client y actualice las referencias de biblioteca del proyecto. Para obtener más información, consulte Instalación de un paquete NuGet. Para usar serializadores de caché de tokens, instale Microsoft. Identity.Web.TokenCache.
Actualice el código según el escenario de cliente confidencial. Algunos pasos son comunes y se aplican en todos los escenarios de cliente confidencial. Otros pasos son únicos para cada escenario.
Escenarios de cliente confidenciales:
Es posible que haya proporcionado un contenedor alrededor de ADAL.NET para controlar los certificados y el almacenamiento en caché. En esta guía se usa el mismo enfoque para ilustrar el proceso de migración de ADAL.NET a MSAL.NET. Sin embargo, este código solo es para fines de demostración. No copie y pegue estos wrappers ni los integre en su código tal como están.
Migración de aplicaciones de demonio
Los escenarios de daemon usan el flujo de OAuth 2.0 de credenciales del cliente. También se denominan llamadas de servicio a servicio. La aplicación adquiere un token en su propio nombre, no en nombre de un usuario.
Averiguar si el código usa escenarios de demonio
El código ADAL de su aplicación usa escenarios de servicio si contiene una llamada a AuthenticationContext.AcquireTokenAsync con los siguientes parámetros:
- Un recurso (URI de identificador de aplicación) como primer parámetro
-
IClientAssertionCertificate o ClientAssertion como segundo parámetro
AuthenticationContext.AcquireTokenAsync no tiene un parámetro de tipo UserAssertion. Si lo tiene, entonces la aplicación es una API web y usa el escenario de API web que llama a las API web de nivel inferior.
Actualiza el código de los escenarios del daemon
Los pasos siguientes para actualizar el código se aplican en todos los escenarios de cliente confidencial:
- Agregue el espacio de nombres MSAL.NET en el código fuente:
using Microsoft.Identity.Client;.
- En lugar de crear una instancia de
AuthenticationContext, use ConfidentialClientApplicationBuilder.Create para crear una instancia de IConfidentialClientApplication.
- En lugar de la
resourceId cadena, MSAL.NET usa ámbitos. Dado que las aplicaciones que usan ADAL.NET están autenticadas previamente, siempre puede usar los siguientes ámbitos: new string[] { $"{resourceId}/.default" }.
- Reemplace la llamada a
AuthenticationContext.AcquireTokenAsync por una llamada a IConfidentialClientApplication.AcquireTokenXXX, donde XXX depende de su escenario.
En este caso, reemplace la llamada a AuthenticationContext.AcquireTokenAsync por una llamada a IConfidentialClientApplication.AcquireTokenClient.
Esta es una comparación del código ADAL.NET y MSAL.NET de escenarios de demonio:
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;
}
}
Aprovecha el almacenamiento en caché de tokens
Si no configura el almacenamiento en caché de tokens, el emisor de tokens le impondrá limitaciones, lo que provocará errores. También se tarda mucho menos en obtener un token de la memoria caché (10-20 ms) de lo que es de ESTS (500-30000 ms).
Si desea implementar una caché de tokens distribuida, consulte Caché de tokens para una aplicación web o api web (aplicación cliente confidencial).
Obtenga más información sobre el escenario de demonio y cómo se implementa con MSAL.NET o Microsoft. Identity.Web en nuevas aplicaciones.
Migración de una API web que llama a las API web de nivel inferior
Las API web que llaman a las API web de nivel inferior usan el flujo con derechos delegados (OBO) de OAuth2.0. La API web usa el token de acceso recuperado del encabezado HTTP Authorize y valida este token. A continuación, este token se intercambia con un token para llamar a la API web de nivel inferior. Este token se usa como instancia UserAssertion de ADAL.NET y MSAL.NET.
Averiguar si el código usa OBO
El código ADAL de la aplicación usa OBO si contiene una llamada a AuthenticationContext.AcquireTokenAsync con los parámetros siguientes:
- Un recurso (URI de identificador de aplicación) como primer parámetro
-
IClientAssertionCertificate o ClientAssertion como segundo parámetro
- Parámetro de tipo
UserAssertion
Actualización del código mediante OBO
Los pasos siguientes para actualizar el código se aplican en todos los escenarios de cliente confidencial:
- Agregue el espacio de nombres MSAL.NET en el código fuente:
using Microsoft.Identity.Client;.
- En lugar de crear una instancia de
AuthenticationContext, use ConfidentialClientApplicationBuilder.Create para crear una instancia de IConfidentialClientApplication.
- En lugar de la
resourceId cadena, MSAL.NET usa ámbitos. Dado que las aplicaciones que usan ADAL.NET están autenticadas previamente, siempre puede usar los siguientes ámbitos: new string[] { $"{resourceId}/.default" }.
- Reemplace la llamada a
AuthenticationContext.AcquireTokenAsync por una llamada a IConfidentialClientApplication.AcquireTokenXXX, donde XXX depende de su escenario.
En este caso, reemplazamos la llamada a AuthenticationContext.AcquireTokenAsync por una llamada a IConfidentialClientApplication.AcquireTokenOnBehalfOf.
Esta es una comparación de código OBO de ejemplo para ADAL.NET y 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;
}
}
Aprovecha el almacenamiento en caché de tokens
Para el almacenamiento en caché de tokens en obOs, use una caché de tokens distribuida. Para más información, consulte Caché de tokens para una aplicación web o una API web (aplicación cliente confidencial).
app.UseInMemoryTokenCaches(); // or a distributed token cache.
Obtenga más información sobre las API web que llaman a las API web de bajada y cómo se implementan con MSAL.NET o Microsoft. Identity.Web en nuevas aplicaciones.
Migración de una aplicación web que llama a las API web
Si la aplicación usa ASP.NET Core, se recomienda encarecidamente actualizar a Microsoft. Identity.Web porque procesa todo por usted. Para una presentación rápida, consulte el anuncio de disponibilidad general de Microsoft.Identity.Web. Para obtener más información sobre cómo usarlo en una aplicación web, consulte ¿Por qué usar Microsoft? Identity.Web en aplicaciones web?
Las aplicaciones web que inician sesión en los usuarios y llaman a las API web en nombre de los usuarios emplean el flujo de código de autorización de OAuth2.0. Típicamente:
- La aplicación inicia la sesión de un usuario ejecutando el primer tramo del flujo de código de autorización al dirigirse al extremo de autorización de la plataforma de identidad de Microsoft. El usuario inicia sesión y realiza autenticaciones multifactor si es necesario. Como resultado de esta operación, la aplicación recibe el código de autorización. La biblioteca de autenticación no se usa en esta fase.
- La aplicación ejecuta la segunda etapa del flujo de código de autorización. Usa el código de autorización para obtener un token de acceso, un token de identificador y un token de actualización. La aplicación debe proporcionar el valor
redirectUri, que es el URI en el que el punto de conexión de la plataforma de identidad de Microsoft proporcionará los tokens de seguridad. Una vez que la aplicación recibe ese URI, normalmente llama AcquireTokenByAuthorizationCode a ADAL o MSAL para canjear el código y obtener un token que se almacenará en la caché de tokens.
- La aplicación usa ADAL o MSAL para llamar a
AcquireTokenSilent y obtener tokens con los que llamar a las API web necesarias desde los controladores de la aplicación web.
Averiguar si el código usa el flujo de código de autenticación
El código ADAL de la aplicación usa el flujo de código de autenticación si contiene una llamada a AuthenticationContext.AcquireTokenByAuthorizationCodeAsync.
Actualización del código mediante el flujo de código de autorización
Los pasos siguientes para actualizar el código se aplican en todos los escenarios de cliente confidencial:
- Agregue el espacio de nombres MSAL.NET en el código fuente:
using Microsoft.Identity.Client;.
- En lugar de crear una instancia de
AuthenticationContext, use ConfidentialClientApplicationBuilder.Create para crear una instancia de IConfidentialClientApplication.
- En lugar de la
resourceId cadena, MSAL.NET usa ámbitos. Dado que las aplicaciones que usan ADAL.NET están autenticadas previamente, siempre puede usar los siguientes ámbitos: new string[] { $"{resourceId}/.default" }.
- Reemplace la llamada a
AuthenticationContext.AcquireTokenAsync por una llamada a IConfidentialClientApplication.AcquireTokenXXX, donde XXX depende de su escenario.
En este caso, reemplace la llamada a AuthenticationContext.AcquireTokenAsync por una llamada a IConfidentialClientApplication.AcquireTokenByAuthorizationCode.
Esta es una comparación de los flujos de código de autorización de ejemplo para ADAL.NET y 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;
}
}
Llamar a AcquireTokenByAuthorizationCode agrega un token a la caché de tokens cuando se recibe el código de autorización. Para adquirir tokens adicionales para otros recursos o inquilinos, use AcquireTokenSilent en los 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;
}
}
Aprovecha el almacenamiento en caché de tokens
Dado que la aplicación web utiliza AcquireTokenByAuthorizationCode, debe usar una caché de tokens distribuida para almacenar en caché los tokens. Para más información, consulte Caché de tokens para una aplicación web o API web.
app.UseInMemoryTokenCaches(); // or a distributed token cache.
Control de la excepción MsalUiRequiredException
Cuando el controlador intenta adquirir un token de forma silenciosa para distintos ámbitos o recursos, MSAL.NET podría generar un MsalUiRequiredException, como es de esperar, si el usuario necesita volver a iniciar sesión o si el acceso al recurso requiere más declaraciones (debido a una directiva de acceso condicional). Para obtener más información sobre la mitigación, consulte cómo controlar errores y excepciones en MSAL.NET.
Obtenga más información sobre las aplicaciones web que llaman a las API web y cómo se implementan con MSAL.NET o Microsoft. Identity.Web en nuevas aplicaciones.
Ventajas de MSAL
Entre las principales ventajas de MSAL.NET para la aplicación se incluyen:
Resistencia. MSAL.NET ayuda a que la aplicación sea resistente a través de:
- Ventajas del Servicio de credenciales en caché (CCS) de Microsoft Entra ID. CCS funciona como una copia de seguridad de Microsoft Entra.
- Renovación proactiva de tokens si la API a la que llama habilita tokens de larga duración a través de Evaluación continua de acceso.
Seguridad Puede adquirir tokens de prueba de posesión (PoP) si la API web a la que desea llamar así lo requiere. Para obtener más información, consulte tokens de prueba de posesión en MSAL.NET
Rendimiento y escalabilidad. Si no necesita compartir la memoria caché con ADAL.NET, deshabilite la compatibilidad de caché heredada al crear la aplicación cliente confidencial (.WithLegacyCacheCompatibility(false)) para aumentar significativamente el rendimiento.
app = ConfidentialClientApplicationBuilder.Create(ClientId)
.WithCertificate(certificate)
.WithAuthority(authority)
.WithLegacyCacheCompatibility(false)
.Build();
Solución de problemas
MsalServiceException
La siguiente información de solución de problemas realiza dos suposiciones:
- El código ADAL.NET estaba funcionando.
- Ha migrado a MSAL manteniendo el mismo identificador de cliente.
Si recibe una excepción con cualquiera de los siguientes mensajes:
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 la excepción mediante estos pasos:
- Confirme que usa la versión más reciente de MSAL.NET.
- Confirme que el host de la autoridad que estableció al crear la aplicación cliente confidencial y el host de la autoridad que usó con ADAL sean similares. En concreto, ¿es la misma nube (Azure Government, Microsoft Azure operada por 21Vianet o Azure Alemania)?
MsalClientException
En las aplicaciones multiinquilino, especifique una autoridad común al compilar la aplicación para tener como destino un inquilino específico, como el inquilino del usuario al llamar a una API web. Desde MSAL.NET 4.37.0, cuando se especifica .WithAzureRegion al crear la aplicación, ya no se puede especificar la autoridad mediante .WithAuthority en las solicitudes de token. Si lo hace, obtendrá el siguiente error al actualizar desde versiones anteriores de 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 corregir este problema, reemplace .WithAuthority en la expresión AcquireTokenXXX por .WithTenantId. Especifique el inquilino mediante un GUID o un nombre de dominio.
Pasos siguientes
Más información sobre: