Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
En esta guía se muestra cómo usar el almacenamiento en caché de tokens de Microsoft.Identity.Web y los conjuntos de certificados con MSAL.NET en .NET Framework, .NET Standard 2.0 y aplicaciones clásicas de .NET (.NET 4.7.2+).
Descripción de la información general
A partir de Microsoft.Identity.Web 1.17+, puede usar los paquetes de utilidad de Microsoft.Identity.Web con MSAL.NET en entornos no ASP.NET Core.
Identificación de las ventajas del paquete
| Feature | Ventajas |
|---|---|
| Serialización de caché de tokens | Adaptadores de caché reutilizables para en memoria, SQL Server, Redis, Cosmos DB, PostgreSQL |
| Asistentes de Certificados | Carga simplificada de certificados desde keyVault, sistema de archivos o almacenes de certificados |
| Extensiones de reclamaciones | Métodos útiles para la manipulación de ClaimsPrincipal |
| .NET Standard 2.0 | Compatible con .NET Framework 4.7.2 y versiones posteriores, .NET Core y .NET 5+ |
| Dependencias mínimas | Paquetes de destino sin dependencias de ASP.NET Core |
Revisión de escenarios admitidos
Los siguientes escenarios se admiten con los paquetes de utilidad de destino.
- .NET Framework Console Applications (escenarios de servicios)
- Desktop Applications (.NET Framework)
- Worker Services (.NET Framework)
- bibliotecas de .NET Standard 2.0 (compatibilidad multiplataforma)
- Aplicaciones MSAL.NET no web
Nota:
Para aplicaciones ASP.NET MVC/Web API, consulte OWIN Integration en su lugar.
Selección de paquetes
Elija el paquete que coincida con su escenario.
Identificación de paquetes principales para MSAL.NET
| Package | propósito | Dependencias | Objetivo de .NET |
|---|---|---|---|
| Microsoft. Identity.Web.TokenCache | Serializadores de caché de tokens, ClaimsPrincipal extensiones |
Mínimo | .NET Standard 2.0 |
| Microsoft. Identity.Web.Certificate | Utilidades de carga de certificados | Mínimo | .NET Standard 2.0 |
Instalación de paquetes
Use uno de los métodos siguientes para agregar los paquetes al proyecto.
Administrador de paquetes Console:
# Token cache serialization
Install-Package Microsoft.Identity.Web.TokenCache
# Certificate management
Install-Package Microsoft.Identity.Web.Certificate
.NET CLI:
dotnet add package Microsoft.Identity.Web.TokenCache
dotnet add package Microsoft.Identity.Web.Certificate
Descripción de las limitaciones del paquete principal
El paquete principal Microsoft.Identity.Web incluye ASP.NET Core dependencias (Microsoft.AspNetCore.*), que:
- No son compatibles con ASP.NET Framework
- Aumentar el tamaño del paquete innecesariamente
- Creación de conflictos de dependencias
Use paquetes de destino en su lugar para escenarios .NET Framework y .NET Estándar.
Configuración de la serialización de caché de tokens
Descripción de los adaptadores de caché de tokens
Microsoft. Identity.Web proporciona adaptadores de caché de tokens que funcionan sin problemas con MSAL.NET IConfidentialClientApplication.
Creación de un cliente confidencial con caché de tokens
En el ejemplo siguiente se crea una aplicación cliente confidencial y se adjunta una caché de tokens en memoria.
using Microsoft.Identity.Client;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.TokenCacheProviders;
public class MsalAppBuilder
{
private static IConfidentialClientApplication _app;
public static IConfidentialClientApplication BuildConfidentialClientApplication()
{
if (_app == null)
{
string clientId = ConfigurationManager.AppSettings["AzureAd:ClientId"];
string clientSecret = ConfigurationManager.AppSettings["AzureAd:ClientSecret"];
string tenantId = ConfigurationManager.AppSettings["AzureAd:TenantId"];
// Create the confidential client application
_app = ConfidentialClientApplicationBuilder.Create(clientId)
.WithClientSecret(clientSecret)
.WithTenantId(tenantId)
.WithAuthority(AzureCloudInstance.AzurePublic, tenantId)
.Build();
// Add token cache serialization (choose one option below)
_app.AddInMemoryTokenCache();
}
return _app;
}
}
Elegir opciones de caché de tokens
Seleccione el proveedor de caché que mejor se adapte a su escenario de implementación.
Configuración de la caché de tokens en memoria
En el ejemplo siguiente se agrega una caché en memoria sencilla:
using Microsoft.Identity.Web.TokenCacheProviders;
_app.AddInMemoryTokenCache();
Caché en memoria con límites de tamaño (Microsoft. Identity.Web 1.20+):
using Microsoft.Extensions.Caching.Memory;
_app.AddInMemoryTokenCache(services =>
{
// Configure memory cache options
services.Configure<MemoryCacheOptions>(options =>
{
options.SizeLimit = 5000000; // 5 MB limit
});
});
Características:
- Acceso rápido
- Sin dependencias externas
- No se comparte entre procesos
- Perdido en el reinicio de la aplicación
Caso de uso: Aplicaciones de consola de instancia única, aplicaciones de escritorio
Configuración de la caché de tokens en memoria distribuida
Use el código siguiente para agregar una caché distribuida en memoria para entornos de varias instancias:
_app.AddDistributedTokenCaches(services =>
{
// Requires: Microsoft.Extensions.Caching.Memory (NuGet)
services.AddDistributedMemoryCache();
});
Características:
- Compartido entre instancias de aplicación
- Mejor para escenarios con equilibrio de carga
- Requiere un paquete NuGet adicional
- Todavía perdido en el reinicio de la aplicación
Caso de uso: Servicios de varias instancias con la readquisición de tokens aceptable
Configuración de la caché de tokens de SQL Server
Use el código siguiente para agregar una caché de SQL Server distribuida persistente:
using Microsoft.Extensions.Caching.SqlServer;
_app.AddDistributedTokenCaches(services =>
{
// Requires: Microsoft.Extensions.Caching.SqlServer (NuGet)
services.AddDistributedSqlServerCache(options =>
{
options.ConnectionString = ConfigurationManager.ConnectionStrings["TokenCache"].ConnectionString;
options.SchemaName = "dbo";
options.TableName = "TokenCache";
// IMPORTANT: Set expiration above token lifetime
// Access tokens typically expire after 1 hour
options.DefaultSlidingExpiration = TimeSpan.FromMinutes(90);
});
});
Ejecute el siguiente código SQL para crear la tabla de caché necesaria:
-- Create the cache table
CREATE TABLE [dbo].[TokenCache] (
[Id] NVARCHAR(449) NOT NULL,
[Value] VARBINARY(MAX) NOT NULL,
[ExpiresAtTime] DATETIMEOFFSET NOT NULL,
[SlidingExpirationInSeconds] BIGINT NULL,
[AbsoluteExpiration] DATETIMEOFFSET NULL,
PRIMARY KEY ([Id])
);
-- Create index for performance
CREATE INDEX [Index_ExpiresAtTime] ON [dbo].[TokenCache] ([ExpiresAtTime]);
Características:
- Persistente entre reinicios
- Compartido entre varias instancias
- Confiable y escalable
- Requiere SQL Server configuración
Caso de uso: Servicios de daemon en producción, tareas programadas, trabajos de múltiples instancias
Configuración de la caché de tokens de Redis
Use el código siguiente para agregar una caché distribuida de Redis de alto rendimiento:
using StackExchange.Redis;
using Microsoft.Extensions.Caching.StackExchangeRedis;
_app.AddDistributedTokenCaches(services =>
{
// Requires: Microsoft.Extensions.Caching.StackExchangeRedis (NuGet)
services.AddStackExchangeRedisCache(options =>
{
options.Configuration = ConfigurationManager.AppSettings["Redis:ConnectionString"];
options.InstanceName = "TokenCache_";
});
});
En el ejemplo siguiente se muestra una configuración de Redis lista para producción:
services.AddStackExchangeRedisCache(options =>
{
options.Configuration = ConfigurationManager.AppSettings["Redis:ConnectionString"];
options.InstanceName = "MyDaemonApp_";
// Optional: Configure Redis options
options.ConfigurationOptions = new ConfigurationOptions
{
AbortOnConnectFail = false,
ConnectTimeout = 5000,
SyncTimeout = 5000
};
});
Características:
- Extremadamente rápido
- Compartido entre instancias
- Persistente (con la persistencia de Redis habilitada)
- Requiere el servidor de Redis
Caso de uso: Aplicaciones de fondo de alto rendimiento, sistemas distribuidos, microservicios
Configuración de la caché de tokens de Cosmos DB
Use el código siguiente para agregar una memoria caché de Cosmos DB distribuida globalmente:
using Microsoft.Extensions.Caching.Cosmos;
_app.AddDistributedTokenCaches(services =>
{
// Requires: Microsoft.Extensions.Caching.Cosmos (preview)
services.AddCosmosCache(options =>
{
options.ContainerName = "TokenCache";
options.DatabaseName = "IdentityCache";
options.ClientBuilder = new CosmosClientBuilder(
ConfigurationManager.AppSettings["CosmosConnectionString"]);
options.CreateIfNotExists = true;
});
});
Características:
- Distribuido globalmente
- Alta disponibilidad
- Escalado automático
- Mayor latencia que Redis
- Mayor costo
Caso de uso: Servicios de daemon globales, aplicaciones con distribución geográfica
Configuración de la caché de tokens de PostgreSQL
Use el código siguiente para agregar una caché distribuida de PostgreSQL:
_app.AddDistributedTokenCaches(services =>
{
// Requires: Microsoft.Extensions.Caching.Postgres (NuGet)
services.AddDistributedPostgresCache(options =>
{
options.ConnectionString = ConfigurationManager.ConnectionStrings["PostgresCache"].ConnectionString;
options.SchemaName = ConfigurationManager.AppSettings["PostgresCache:SchemaName"];
options.TableName = ConfigurationManager.AppSettings["PostgresCache:TableName"];
options.CreateIfNotExists = bool.Parse(
ConfigurationManager.AppSettings["PostgresCache:CreateIfNotExists"] ?? "true");
// Set expiration above token lifetime.
// Access tokens typically expire after 1 hour.
options.DefaultSlidingExpiration = TimeSpan.FromMinutes(90);
});
});
Características:
- Persistente entre reinicios
- Compartido entre varias instancias
- Semántica de SQL familiar
- Funciona con Azure Database for PostgreSQL
- Requiere un servidor PostgreSQL
Caso de uso: Aplicaciones que ya usan PostgreSQL como base de datos principal o servicios alojados en Azure que utilizan Azure Database for PostgreSQL
Creación de una aplicación daemon completa
En el ejemplo siguiente se muestra una aplicación de demonio completa que adquiere tokens mediante credenciales de cliente y una caché de tokens de SQL Server.
using Microsoft.Identity.Client;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.TokenCacheProviders;
using System;
using System.Threading.Tasks;
namespace DaemonApp
{
class Program
{
private static IConfidentialClientApplication _app;
static async Task Main(string[] args)
{
// Build confidential client with token cache
_app = BuildConfidentialClient();
// Acquire token for app-only access
string[] scopes = new[] { "https://graph.microsoft.com/.default" };
try
{
var result = await _app.AcquireTokenForClient(scopes)
.ExecuteAsync();
Console.WriteLine($"Token acquired successfully!");
Console.WriteLine($"Token source: {result.AuthenticationResultMetadata.TokenSource}");
Console.WriteLine($"Expires on: {result.ExpiresOn}");
// Use token to call API
await CallProtectedApi(result.AccessToken);
}
catch (MsalServiceException ex)
{
Console.WriteLine($"Error acquiring token: {ex.ErrorCode}");
Console.WriteLine($"CorrelationId: {ex.CorrelationId}");
}
}
private static IConfidentialClientApplication BuildConfidentialClient()
{
var app = ConfidentialClientApplicationBuilder
.Create(ConfigurationManager.AppSettings["ClientId"])
.WithClientSecret(ConfigurationManager.AppSettings["ClientSecret"])
.WithTenantId(ConfigurationManager.AppSettings["TenantId"])
.Build();
// Add SQL Server token cache for persistence
app.AddDistributedTokenCaches(services =>
{
services.AddDistributedSqlServerCache(options =>
{
options.ConnectionString = ConfigurationManager
.ConnectionStrings["TokenCache"].ConnectionString;
options.SchemaName = "dbo";
options.TableName = "TokenCache";
options.DefaultSlidingExpiration = TimeSpan.FromMinutes(90);
});
});
return app;
}
private static async Task CallProtectedApi(string accessToken)
{
// Your API call logic
}
}
}
Administración de certificados
Comprender la carga de certificados
Microsoft. Identity.Web simplifica la carga de certificados desde varios orígenes para los flujos de credenciales de cliente.
Carga de certificados con DefaultCertificateLoader
En el ejemplo siguiente se muestra cómo cargar un certificado desde Azure Key Vault y crear una aplicación cliente confidencial.
using Microsoft.Identity.Web;
using Microsoft.Identity.Client;
public class CertificateHelper
{
public static IConfidentialClientApplication CreateAppWithCertificate()
{
string clientId = ConfigurationManager.AppSettings["AzureAd:ClientId"];
string tenantId = ConfigurationManager.AppSettings["AzureAd:TenantId"];
// Define certificate source
var certDescription = CertificateDescription.FromKeyVault(
keyVaultUrl: "https://my-keyvault.vault.azure.net",
keyVaultCertificateName: "MyCertificate"
);
// Load certificate
ICertificateLoader certificateLoader = new DefaultCertificateLoader();
certificateLoader.LoadIfNeeded(certDescription);
// Create confidential client with certificate
var app = ConfidentialClientApplicationBuilder.Create(clientId)
.WithCertificate(certDescription.Certificate)
.WithTenantId(tenantId)
.Build();
// Add token cache
app.AddInMemoryTokenCache();
return app;
}
}
Elegir orígenes de certificados
Cargar desde Azure Key Vault
Cargue un certificado almacenado en Azure Key Vault especificando la dirección URL del almacén y el nombre del certificado.
var certDescription = CertificateDescription.FromKeyVault(
keyVaultUrl: "https://my-keyvault.vault.azure.net",
keyVaultCertificateName: "MyApplicationCert"
);
ICertificateLoader loader = new DefaultCertificateLoader();
loader.LoadIfNeeded(certDescription);
var app = ConfidentialClientApplicationBuilder.Create(clientId)
.WithCertificate(certDescription.Certificate)
.WithTenantId(tenantId)
.Build();
Requisitos previos:
- Identidad administrada o Principal de servicio con acceso a Key Vault
- paquete NuGet
Azure.Identity - El permiso de Key Vault:
Getpara certificados
Cargar desde el almacén de certificados
Cargue un certificado del almacén de certificados de Windows por nombre distintivo.
var certDescription = CertificateDescription.FromStoreWithDistinguishedName(
distinguishedName: "CN=MyApp.contoso.com",
storeName: StoreName.My,
storeLocation: StoreLocation.CurrentUser
);
ICertificateLoader loader = new DefaultCertificateLoader();
loader.LoadIfNeeded(certDescription);
var app = ConfidentialClientApplicationBuilder.Create(clientId)
.WithCertificate(certDescription.Certificate)
.WithTenantId(tenantId)
.Build();
También puede encontrar un certificado mediante huella digital:
var certDescription = CertificateDescription.FromStoreWithThumbprint(
thumbprint: "ABCDEF1234567890ABCDEF1234567890ABCDEF12",
storeName: StoreName.My,
storeLocation: StoreLocation.LocalMachine
);
Cargar desde el sistema de archivos
Cargue un certificado desde un archivo PFX en el sistema de archivos local.
var certDescription = CertificateDescription.FromPath(
path: @"C:\Certificates\MyAppCert.pfx",
password: ConfigurationManager.AppSettings["Certificate:Password"]
);
ICertificateLoader loader = new DefaultCertificateLoader();
loader.LoadIfNeeded(certDescription);
var app = ConfidentialClientApplicationBuilder.Create(clientId)
.WithCertificate(certDescription.Certificate)
.WithTenantId(tenantId)
.Build();
Nota de seguridad: Nunca codifique contraseñas de forma dura. Use la configuración segura.
Cargar desde una cadena codificada en Base64
Cargue un certificado de una cadena codificada en Base64 almacenada en la configuración.
string base64Cert = ConfigurationManager.AppSettings["Certificate:Base64"];
var certDescription = CertificateDescription.FromBase64Encoded(
base64EncodedValue: base64Cert,
password: ConfigurationManager.AppSettings["Certificate:Password"] // Optional
);
ICertificateLoader loader = new DefaultCertificateLoader();
loader.LoadIfNeeded(certDescription);
Configuración de la carga de certificados desde App.config
Defina los ajustes del certificado en el archivo App.config y cárguelos en tiempo de ejecución.
App.config:
<appSettings>
<add key="AzureAd:ClientId" value="your-client-id" />
<add key="AzureAd:TenantId" value="your-tenant-id" />
<!-- Option 1: KeyVault -->
<add key="Certificate:SourceType" value="KeyVault" />
<add key="Certificate:KeyVaultUrl" value="https://my-vault.vault.azure.net" />
<add key="Certificate:KeyVaultCertificateName" value="MyCert" />
<!-- Option 2: Store -->
<!--
<add key="Certificate:SourceType" value="StoreWithThumbprint" />
<add key="Certificate:CertificateThumbprint" value="ABCD..." />
<add key="Certificate:CertificateStorePath" value="CurrentUser/My" />
-->
</appSettings>
<connectionStrings>
<add name="TokenCache"
connectionString="Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=TokenCache;Integrated Security=True;" />
</connectionStrings>
Use el siguiente método auxiliar para cargar el certificado en función de la configuración:
public static CertificateDescription GetCertificateFromConfig()
{
string sourceType = ConfigurationManager.AppSettings["Certificate:SourceType"];
return sourceType switch
{
"KeyVault" => CertificateDescription.FromKeyVault(
ConfigurationManager.AppSettings["Certificate:KeyVaultUrl"],
ConfigurationManager.AppSettings["Certificate:KeyVaultCertificateName"]
),
"StoreWithThumbprint" => CertificateDescription.FromStoreWithThumbprint(
ConfigurationManager.AppSettings["Certificate:CertificateThumbprint"],
StoreName.My,
StoreLocation.CurrentUser
),
_ => throw new ConfigurationErrorsException("Invalid certificate source type")
};
}
Exploración de aplicaciones de ejemplo
Revise estos ejemplos para ver las implementaciones de trabajo.
Revisar ejemplos oficiales de Microsoft
En la tabla siguiente se enumeran ejemplos oficiales que muestran el almacenamiento en caché de tokens y la carga de certificados.
| Ejemplo | Plataforma | Descripción |
|---|---|---|
| ConfidentialClientTokenCache | Consola (.NET Framework) | Patrones de serialización de caché de tokens |
| active-directory-dotnetcore-daemon-v2 | Consola (.NET Core) | Carga de certificados desde Key Vault |
Seguimiento de los procedimientos recomendados
Aplique estos patrones para crear aplicaciones confiables y seguras.
Seguir patrones recomendados
1. Use el patrón singleton para IConfidentialClientApplication:
Cree una sola instancia y reutilícela en la aplicación.
private static IConfidentialClientApplication _app;
public static IConfidentialClientApplication GetApp()
{
if (_app == null)
{
_app = ConfidentialClientApplicationBuilder.Create(clientId)
.WithClientSecret(clientSecret)
.WithTenantId(tenantId)
.Build();
_app.AddDistributedTokenCaches(/* ... */);
}
return _app;
}
2. Establecer la expiración de caché de tokens adecuada:
Configure la expiración deslizante para que sea mayor que la duración del token y así evitar la readquisición innecesaria.
// Access tokens typically expire after 1 hour
// Set cache expiration ABOVE token lifetime
options.DefaultSlidingExpiration = TimeSpan.FromMinutes(90);
3. Use almacenamiento seguro de certificados:
Almacene certificados en Azure Key Vault o en un almacén de certificados protegido correctamente.
// Azure Key Vault (production)
var cert = CertificateDescription.FromKeyVault(keyVaultUrl, certName);
// Certificate store with proper permissions
var cert = CertificateDescription.FromStoreWithThumbprint(
thumbprint, StoreName.My, StoreLocation.LocalMachine);
4. Implemente el control de errores adecuado:
Capture excepciones de MSAL y registre el identificador de correlación para solucionar problemas.
try
{
var result = await app.AcquireTokenForClient(scopes).ExecuteAsync();
}
catch (MsalServiceException ex)
{
logger.Error($"Token acquisition failed. CorrelationId: {ex.CorrelationId}, ErrorCode: {ex.ErrorCode}");
throw;
}
5. Use una caché distribuida para producción:
Una caché distribuida comparte tokens entre instancias y persiste en los reinicios.
// Correct for daemon services
app.AddDistributedTokenCaches(services =>
{
services.AddDistributedSqlServerCache(/* ... */);
});
Evitar errores comunes
1. No cree nuevas instancias IConfidentialClientApplication repetidamente:
// Wrong - creates new instance every time
public void AcquireToken()
{
var app = ConfidentialClientApplicationBuilder.Create(clientId).Build();
// ...
}
// Correct - use singleton
private static readonly IConfidentialClientApplication _app = BuildApp();
2. No codifique los secretos de forma dura:
// Wrong
.WithClientSecret("supersecretvalue123")
// Correct
.WithClientSecret(ConfigurationManager.AppSettings["AzureAd:ClientSecret"])
3. No use la memoria caché en memoria para los servicios de varias instancias:
// Wrong for services with multiple instances
app.AddInMemoryTokenCache();
// Correct - use distributed cache
app.AddDistributedTokenCaches(services =>
{
services.AddDistributedSqlServerCache(/* ... */);
});
4. No ignore la validación de certificados:
// Wrong - skips validation
ServicePointManager.ServerCertificateValidationCallback = (sender, cert, chain, errors) => true;
// Correct - validate certificates properly
Migración desde ADAL.NET
Revise las diferencias clave y actualice el código para usar MSAL.NET con Microsoft. Identity.Web.
Descripción de las diferencias clave
| Aspecto | ADAL.NET (en desuso) | MSAL.NET + Microsoft. Identity.Web |
|---|---|---|
| Ámbitos | Basado en recursos (https://graph.microsoft.com) |
Basado en ámbito (https://graph.microsoft.com/.default) |
| Caché de tokens | Serialización manual necesaria | Adaptadores integrados mediante métodos de extensión |
| Certificados | Carga manual de X509Certificate2 |
DefaultCertificateLoader con varios orígenes |
| Autoridad | Fijado durante la construcción | Se puede invalidar por solicitud |
Comparación de ejemplos de migración
ADAL.NET (Antiguo):
AuthenticationContext authContext = new AuthenticationContext(authority);
ClientCredential credential = new ClientCredential(clientId, clientSecret);
AuthenticationResult result = await authContext.AcquireTokenAsync(resource, credential);
MSAL.NET con Microsoft. Identity.Web (Nuevo):
var app = ConfidentialClientApplicationBuilder.Create(clientId)
.WithClientSecret(clientSecret)
.WithTenantId(tenantId)
.Build();
app.AddInMemoryTokenCache(); // Add token cache
string[] scopes = new[] { "https://graph.microsoft.com/.default" };
AuthenticationResult result = await app.AcquireTokenForClient(scopes).ExecuteAsync();
Exploración del contenido relacionado
Use estos recursos para obtener más información sobre los escenarios relacionados.
- Aplicaciones de demonio
- Integración de OWIN
- Información general de ASP.NET Framework
- Introducción a las credenciales