Crear aplicaciones de tipo daemon e identidades de agente con Microsoft.Identity.Web

En este artículo, construyes aplicaciones daemon, servicios en segundo plano y agentes autónomos usando Microsoft.Identity.Web. Estas aplicaciones se ejecutan sin interacción del usuario y se autentican mediante identidades de aplicación (credenciales de cliente) o identidades de agente.

Descripción de los escenarios admitidos

Microsoft. Identity.Web admite tres tipos de aplicaciones no interactivas:

Escenario Tipo de autenticación Tipo de token Caso de uso
Demonio estándar Credenciales de cliente (secreto/certificado) Token de acceso de solo aplicación Servicios en segundo plano, trabajos programados, procesamiento de datos
Agente autónomo Identidad del agente con credenciales de cliente Token de acceso de solo aplicación para el agente Agentes Copilot, servicios autónomos que actúan en nombre de una identidad de agente. (Normalmente en una API web protegida)
Identidad de usuario del agente Identidad de usuario del agente Identidad de usuario del agente con credenciales de cliente Servicios autónomos que actúan en nombre de una identidad de usuario agente. (Normalmente en una API web protegida)

Comenzar

Prerrequisitos

Antes de comenzar, asegúrese de que tiene:

  • .NET 8.0 o posterior
  • Microsoft Entra registro de aplicaciones con credenciales de cliente (secreto de cliente o certificado)
  • Para escenarios de agente: identidades de agente configuradas en el inquilino de Microsoft Entra

Instalación de paquetes

Agregue los paquetes NuGet necesarios al proyecto:

dotnet add package Microsoft.Identity.Web
dotnet add package Microsoft.Extensions.Hosting

Elección de un enfoque de configuración

Microsoft.Identity.Web proporciona dos maneras de configurar aplicaciones de servicio:

Lo mejor para: Prototipos rápidos, aplicaciones de consola, pruebas, y servicios en segundo plano simples.

El código siguiente crea un TokenAcquirerFactory, configura las API de bajada y Microsoft Graph y llama a la API de Graph:

using Microsoft.Identity.Abstractions;
using Microsoft.Identity.Web;

// Get the token acquirer factory instance
var tokenAcquirerFactory = TokenAcquirerFactory.GetDefaultInstance();

// Configure downstream API and Microsoft Graph (optional)
tokenAcquirerFactory.Services.AddDownstreamApis(
    tokenAcquirerFactory.Configuration.GetSection("DownstreamApis"))
    .AddMicrosoftGraph();

var serviceProvider = tokenAcquirerFactory.Build();

// Call Microsoft Graph
var graphClient = serviceProvider.GetRequiredService<GraphServiceClient>();
var users = await graphClient.Users.GetAsync();

Ventajas:

  • Código estándar mínimo
  • Carga automáticamente appsettings.json
  • Perfecto para escenarios sencillos
  • Inicialización de una línea

Desventajas:

  • No es adecuado para las pruebas que se ejecutan en paralelo (singleton)

Lo mejor para: Aplicaciones de producción, escenarios complejos, inserción de dependencias, capacidad de prueba.

El código siguiente usa el host genérico de .NET para configurar la autenticación, la adquisición de tokens, el almacenamiento en caché y un servicio en segundo plano:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Identity.Web;

var host = Host.CreateDefaultBuilder(args)
    .ConfigureServices((context, services) =>
    {
        // Configure authentication
        services.Configure<MicrosoftIdentityApplicationOptions>(
            context.Configuration.GetSection("AzureAd"));

        // Add token acquisition (true = singleton lifetime)
        services.AddTokenAcquisition(true);

        // Add token cache (in-memory for development)
        services.AddInMemoryTokenCaches();

        // Add HTTP client for API calls
        services.AddHttpClient();

        // Add Microsoft Graph (optional)
        services.AddMicrosoftGraph();

        // Add your background service
        services.AddHostedService<DaemonWorker>();
    })
    .Build();

await host.RunAsync();

Ventajas:

  • Control total sobre los proveedores de configuración
  • Mejor capacidad de prueba con inserción de constructores
  • Se integra con ASP.NET Core modelo de hospedaje
  • Admite escenarios complejos (varios esquemas de autenticación)
  • Arquitectura lista para producción
  • Admite la ejecución de pruebas paralelas (proveedor de servicios aislado por prueba)

Nota:

El parámetro true de AddTokenAcquisition(true) significa que el servicio se registra como singleton (instancia única para la vida útil de la aplicación). Utilice false para la vida útil delimitada en aplicaciones web.

Recomendación: Comience con TokenAcquirerFactory para prototipos y pruebas de un solo subproceso. Migre al patrón completo ServiceCollection al compilar aplicaciones de producción o ejecutar pruebas paralelas.


Configura aplicaciones de demonio estándar

Las aplicaciones de demonio estándar se autentican mediante credenciales de cliente (secreto de cliente o certificado) y obtienen tokens de acceso de solo aplicación para llamar a las API.

Configurar los ajustes de autenticación

Agregue la siguiente configuración al archivo appsettings.json . Puede usar un secreto de cliente o un certificado (recomendado para producción):

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantId": "your-tenant-id",
    "ClientId": "your-client-id",

    "ClientSecret": "your-client-secret",

    "ClientCredentials": [
      // Option 1: Client Secret
      {
        "SourceType": "ClientSecret",
        "ClientSecret": "your-client-secret",
      },
      // Option 2: Certificate (recommended for production)
      {
        "SourceType": "StoreWithDistinguishedName",
        "CertificateStorePath": "CurrentUser/My",
        "CertificateDistinguishedName": "CN=DaemonAppCert"
      }
      // More options: https://aka.ms/ms-id-web/client-credentials
    ]
  }
}

Importante: Establezca su appsettings.json para copiar en el directorio de salida. Agregue lo siguiente al .csproj archivo:

<ItemGroup>
  <None Update="appsettings.json">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
  </None>
</ItemGroup>

Las aplicaciones de ASP.NET Core copian este archivo automáticamente, pero las aplicaciones daemon (y las aplicaciones OWIN) no.

Configuración del servicio

El siguiente código Program.cs registra Microsoft opciones de identidad, adquisición de tokens, almacenamiento en caché y un servicio en segundo plano hospedado:

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Identity.Web;

var host = Host.CreateDefaultBuilder(args)
    .ConfigureServices((context, services) =>
    {
        IConfiguration configuration = context.Configuration;

        // Configure Microsoft Identity options
        services.Configure<MicrosoftIdentityApplicationOptions>(
            configuration.GetSection("AzureAd"));

        // Add token acquisition (true = singleton)
        services.AddTokenAcquisition(true);

        // Add token cache
        services.AddInMemoryTokenCaches(); // For development
        // services.AddDistributedTokenCaches(); // For production

        // Add HTTP client
        services.AddHttpClient();

        // Add Microsoft Graph SDK (optional)
        services.AddMicrosoftGraph();

        // Add your background service
        services.AddHostedService<DaemonWorker>();
    })
    .Build();

await host.RunAsync();

Llame a Microsoft Graph

La siguiente clase DaemonWorker.cs usa el SDK de Graph para mostrar a los usuarios una programación periódica:

using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Graph;
using Microsoft.Identity.Abstractions;

public class DaemonWorker : BackgroundService
{
    private readonly GraphServiceClient _graphClient;
    private readonly ILogger<DaemonWorker> _logger;

    public DaemonWorker(
        GraphServiceClient graphClient,
        ILogger<DaemonWorker> logger)
    {
        _graphClient = graphClient;
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            try
            {
                // Call Microsoft Graph with app-only permissions
                var users = await _graphClient.Users
                    .GetAsync(cancellationToken: stoppingToken);

                _logger.LogInformation($"Found {users?.Value?.Count} users");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error calling Microsoft Graph");
            }

            await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken);
        }
    }
}

Uso de IAuthorizationHeaderProvider

Para obtener más control sobre las llamadas HTTP, use IAuthorizationHeaderProvider para crear encabezados de autorización manualmente:

using Microsoft.Identity.Abstractions;

public class DaemonService
{
    private readonly IAuthorizationHeaderProvider _authProvider;
    private readonly HttpClient _httpClient;

    public DaemonService(
        IAuthorizationHeaderProvider authProvider,
        IHttpClientFactory httpClientFactory)
    {
        _authProvider = authProvider;
        _httpClient = httpClientFactory.CreateClient();
    }

    public async Task<string> CallApiAsync()
    {
        // Get authorization header for app-only access
        string authHeader = await _authProvider
            .CreateAuthorizationHeaderForAppAsync(
                scopes: "https://graph.microsoft.com/.default");

        // Add to HTTP request
        _httpClient.DefaultRequestHeaders.Clear();
        _httpClient.DefaultRequestHeaders.Add("Authorization", authHeader);

        var response = await _httpClient.GetStringAsync(
            "https://graph.microsoft.com/v1.0/users");

        return response;
    }
}

Consulte también Llamadas a las API subyacentes para obtener información sobre todas las maneras en que Microsoft Identity Web propone llamar a las API de servidor.


Configuración de agentes autónomos (identidad del agente)

Los agentes autónomos usan identidades de agente para obtener tokens de solo aplicación. Este patrón es útil para escenarios de Copilot y servicios autónomos.

Nota:

Microsoft recomienda que los agentes que llaman a las API de bajada lo hagan desde las API web protegidas, incluso cuando los agentes adquieren un token de aplicación.

Configuración de servicios de agente

El código siguiente configura la autenticación, la adquisición de tokens y la compatibilidad con la identidad del agente mediante la configuración en memoria:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Identity.Web;

var services = new ServiceCollection();

// Configuration
var configuration = new ConfigurationBuilder()
    .AddInMemoryCollection(new Dictionary<string, string?>
    {
        ["AzureAd:Instance"] = "https://login.microsoftonline.com/",
        ["AzureAd:TenantId"] = "your-tenant-id",
        ["AzureAd:ClientId"] = "your-agent-app-client-id",
        ["AzureAd:ClientCredentials:0:SourceType"] = "StoreWithDistinguishedName",
        ["AzureAd:ClientCredentials:0:CertificateStorePath"] = "CurrentUser/My",
        ["AzureAd:ClientCredentials:0:CertificateDistinguishedName"] = "CN=YourCert"
    })
    .Build();

services.AddSingleton<IConfiguration>(configuration);

// Configure Microsoft Identity
services.Configure<MicrosoftIdentityApplicationOptions>(
    configuration.GetSection("AzureAd"));

services.AddTokenAcquisition(true);
services.AddInMemoryTokenCaches();
services.AddHttpClient();
services.AddMicrosoftGraph();

// Add agent identities support
services.AddAgentIdentities();

var serviceProvider = services.BuildServiceProvider();

Adquisición de tokens con la identidad del agente

Después de configurar los servicios del agente, adquiera tokens mediante IAuthorizationHeaderProvider o el SDK de Microsoft Graph:

using Microsoft.Identity.Abstractions;
using Microsoft.Graph;

// Your agent identity GUID
string agentIdentityId = "d84da24a-2ea2-42b8-b5ab-8637ec208024";

// Option 1: Using IAuthorizationHeaderProvider
IAuthorizationHeaderProvider authProvider =
    serviceProvider.GetRequiredService<IAuthorizationHeaderProvider>();

var options = new AuthorizationHeaderProviderOptions()
    .WithAgentIdentity(agentIdentityId);

string authHeader = await authProvider.CreateAuthorizationHeaderForAppAsync(
    scopes: "https://graph.microsoft.com/.default",
    options);

// Option 2: Using Microsoft Graph SDK
GraphServiceClient graphClient =
    serviceProvider.GetRequiredService<GraphServiceClient>();

var applications = await graphClient.Applications.GetAsync(request =>
{
    request.Options.WithAuthenticationOptions(authOptions =>
    {
        authOptions.WithAgentIdentity(agentIdentityId);
    });
});

Revisión de un ejemplo completo de agente autónomo

La siguiente clase encapsula la adquisición de token de identidad del agente y llamadas a la API de Graph en un servicio reutilizable.

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Graph;
using Microsoft.Identity.Abstractions;
using Microsoft.Identity.Web;

public class AutonomousAgentService
{
    private readonly GraphServiceClient _graphClient;
    private readonly IAuthorizationHeaderProvider _authProvider;
    private readonly string _agentIdentityId;

    public AutonomousAgentService(
        string agentIdentityId,
        IServiceProvider serviceProvider)
    {
        _agentIdentityId = agentIdentityId;
        _graphClient = serviceProvider.GetRequiredService<GraphServiceClient>();
        _authProvider = serviceProvider.GetRequiredService<IAuthorizationHeaderProvider>();
    }

    public async Task<string> GetAuthorizationHeaderAsync()
    {
        var options = new AuthorizationHeaderProviderOptions()
            .WithAgentIdentity(_agentIdentityId);

        return await _authProvider.CreateAuthorizationHeaderForAppAsync(
            "https://graph.microsoft.com/.default",
            options);
    }

    public async Task<IEnumerable<Application>> ListApplicationsAsync()
    {
        var apps = await _graphClient.Applications.GetAsync(request =>
        {
            request.Options.WithAuthenticationOptions(options =>
            {
                options.WithAgentIdentity(_agentIdentityId);
            });
        });

        return apps?.Value ?? Enumerable.Empty<Application>();
    }
}

Configuración de la identidad de usuario del agente

La identidad de usuario del agente permite a los agentes actuar en nombre de un usuario del agente con permisos delegados. Use este patrón para los agentes que necesitan su propio buzón u otros recursos con ámbito de usuario.

Prerrequisitos

Para usar la identidad de usuario del agente, necesita:

  • Plano técnico del agente registrado en Microsoft Entra ID
  • Identidad del agente creada y vinculada a la aplicación del agente
  • Identidad de usuario del agente asociada a la identidad del agente

Configuración de los servicios de usuario del agente

El código siguiente configura la identidad de la aplicación del agente con una credencial de certificado y registra los servicios necesarios:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Identity.Web;
using System.Security.Cryptography.X509Certificates;

var services = new ServiceCollection();

// Configure agent application
services.Configure<MicrosoftIdentityApplicationOptions>(options =>
{
    options.Instance = "https://login.microsoftonline.com/";
    options.TenantId = "your-tenant-id";
    options.ClientId = "your-agent-app-client-id";

    // Use certificate for agent authentication
    options.ClientCredentials = new[]
    {
        CertificateDescription.FromStoreWithDistinguishedName(
            "CN=YourCertificate",
            StoreLocation.CurrentUser,
            StoreName.My)
    };
});

// Add services (true = singleton)
services.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build());
services.AddTokenAcquisition(true);
services.AddInMemoryTokenCaches();
services.AddHttpClient();
services.AddMicrosoftGraph();
services.AddAgentIdentities();

var serviceProvider = services.BuildServiceProvider();

Adquisición de tokens de usuario con identidad de agente

Puede identificar el usuario de destino por UPN o identificador de objeto.

Por nombre de usuario (UPN)

using Microsoft.Identity.Abstractions;
using Microsoft.Graph;

string agentIdentityId = "your-agent-identity-id";
string userUpn = "user@yourtenant.onmicrosoft.com";

// Get authorization header
IAuthorizationHeaderProvider authProvider =
    serviceProvider.GetRequiredService<IAuthorizationHeaderProvider>();

var options = new AuthorizationHeaderProviderOptions()
    .WithAgentUserIdentity(
        agentApplicationId: agentIdentityId,
        username: userUpn);

string authHeader = await authProvider.CreateAuthorizationHeaderForUserAsync(
    scopes: new[] { "https://graph.microsoft.com/.default" },
    options);

// Or use Microsoft Graph SDK
GraphServiceClient graphClient =
    serviceProvider.GetRequiredService<GraphServiceClient>();

var me = await graphClient.Me.GetAsync(request =>
{
    request.Options.WithAuthenticationOptions(options =>
        options.WithAgentUserIdentity(agentIdentityId, userUpn));
});

Por identificador de objeto de usuario

string agentIdentityId = "your-agent-identity-id";
Guid userObjectId = Guid.Parse("user-object-id");

var options = new AuthorizationHeaderProviderOptions()
    .WithAgentUserIdentity(
        agentApplicationId: agentIdentityId,
        userId: userObjectId);

string authHeader = await authProvider.CreateAuthorizationHeaderForUserAsync(
    scopes: new[] { "https://graph.microsoft.com/.default" },
    options);

// With Graph SDK
var me = await graphClient.Me.GetAsync(request =>
{
    request.Options.WithAuthenticationOptions(options =>
        options.WithAgentUserIdentity(agentIdentityId, userObjectId));
});

Almacenar tokens en caché con ClaimsPrincipal

Para mejorar el rendimiento, almacene en caché los tokens de usuario mediante una instancia ClaimsPrincipal. La primera llamada rellena la entidad de seguridad con las afirmaciones uid y utid; las llamadas posteriores reutilizan el token almacenado en caché.

using System.Security.Claims;
using Microsoft.Identity.Abstractions;

// First call - creates cache entry
ClaimsPrincipal userPrincipal = new ClaimsPrincipal();

string authHeader = await authProvider.CreateAuthorizationHeaderForUserAsync(
    scopes: new[] { "https://graph.microsoft.com/.default" },
    options,
    userPrincipal);

// ClaimsPrincipal now has uid and utid claims for caching
bool hasUserId = userPrincipal.HasClaim(c => c.Type == "uid");
bool hasTenantId = userPrincipal.HasClaim(c => c.Type == "utid");

// Subsequent calls - uses cache
authHeader = await authProvider.CreateAuthorizationHeaderForUserAsync(
    scopes: new[] { "https://graph.microsoft.com/.default" },
    options,
    userPrincipal); // Reuse the same principal

Invalidación del inquilino

En escenarios multiinquilino, puede invalidar el inquilino en tiempo de ejecución. Esto resulta útil cuando la aplicación está configurada con "common" , pero debe tener como destino un inquilino específico:

var options = new AuthorizationHeaderProviderOptions()
    .WithAgentUserIdentity(agentIdentityId, userUpn);

// Override tenant (useful when app is configured with "common")
options.AcquireTokenOptions.Tenant = "specific-tenant-id";

string authHeader = await authProvider.CreateAuthorizationHeaderForUserAsync(
    scopes: new[] { "https://graph.microsoft.com/.default" },
    options);

// With Graph SDK
var me = await graphClient.Me.GetAsync(request =>
{
    request.Options.WithAuthenticationOptions(options =>
    {
        options.WithAgentUserIdentity(agentIdentityId, userUpn);
        options.AcquireTokenOptions.Tenant = "specific-tenant-id";
    });
});

Revisión de un ejemplo completo de identidad de usuario del agente

La siguiente clase proporciona métodos para obtener perfiles de usuario y encabezados de autorización mediante la identidad de usuario del agente:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Graph;
using Microsoft.Identity.Abstractions;
using System.Security.Claims;

public class AgentUserService
{
    private readonly IAuthorizationHeaderProvider _authProvider;
    private readonly GraphServiceClient _graphClient;
    private readonly string _agentIdentityId;

    public AgentUserService(
        string agentIdentityId,
        IServiceProvider serviceProvider)
    {
        _agentIdentityId = agentIdentityId;
        _authProvider = serviceProvider.GetRequiredService<IAuthorizationHeaderProvider>();
        _graphClient = serviceProvider.GetRequiredService<GraphServiceClient>();
    }

    public async Task<User> GetUserProfileAsync(string userUpn)
    {
        var me = await _graphClient.Me.GetAsync(request =>
        {
            request.Options.WithAuthenticationOptions(options =>
                options.WithAgentUserIdentity(_agentIdentityId, userUpn));
        });

        return me!;
    }

    public async Task<User> GetUserProfileByIdAsync(Guid userObjectId)
    {
        var me = await _graphClient.Me.GetAsync(request =>
        {
            request.Options.WithAuthenticationOptions(options =>
                options.WithAgentUserIdentity(_agentIdentityId, userObjectId));
        });

        return me!;
    }

    public async Task<string> GetAuthHeaderForUserAsync(
        string userUpn,
        ClaimsPrincipal? cachedPrincipal = null)
    {
        var options = new AuthorizationHeaderProviderOptions()
            .WithAgentUserIdentity(_agentIdentityId, userUpn);

        return await _authProvider.CreateAuthorizationHeaderForUserAsync(
            scopes: new[] { "https://graph.microsoft.com/.default" },
            options,
            cachedPrincipal ?? new ClaimsPrincipal());
    }
}

Creación de una configuración de servicio reutilizable

Definición de un método de extensión

Cree un método de extensión reutilizable para encapsular la configuración de identidad del agente en toda la aplicación:

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.TokenCacheProviders.InMemory;

public static class ServiceCollectionExtensions
{
    public static IServiceProvider ConfigureServicesForAgentIdentities(
        this IServiceCollection services,
        IConfiguration configuration)
    {
        // Add configuration
        services.AddSingleton(configuration);

        // Configure Microsoft Identity options
        services.Configure<MicrosoftIdentityApplicationOptions>(
            configuration.GetSection("AzureAd"));

        services.AddTokenAcquisition(true);

        // Add token caching
        services.AddInMemoryTokenCaches();

        // Add HTTP client
        services.AddHttpClient();

        // Add Microsoft Graph (optional)
        services.AddMicrosoftGraph();

        // Add agent identities support
        services.AddAgentIdentities();

        return services.BuildServiceProvider();
    }
}

Uso del método de extensión

Llame al método de extensión para configurar los servicios en una sola línea:

var services = new ServiceCollection();
var configuration = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json")
    .Build();

var serviceProvider = services.ConfigureServicesForAgentIdentities(configuration);

Llamada a las API

En esta sección se muestra cómo llamar a las API mediante cada uno de los tres patrones de autenticación.

Llame a Microsoft Graph

En los ejemplos siguientes se muestra cómo llamar a Microsoft Graph como demonio estándar, un agente autónomo y una identidad de usuario del agente:

using Microsoft.Graph;

GraphServiceClient graphClient =
    serviceProvider.GetRequiredService<GraphServiceClient>();

// Standard daemon (app-only)
var users = await graphClient.Users.GetAsync();

// Autonomous agent (app-only with agent identity)
var apps = await graphClient.Applications.GetAsync(request =>
{
    request.Options.WithAuthenticationOptions(options =>
    {
        options.WithAgentIdentity("agent-identity-id");
        options.RequestAppToken = true;
    });
});

// Agent user identity (delegated with user context)
var me = await graphClient.Me.GetAsync(request =>
{
    request.Options.WithAuthenticationOptions(options =>
        options.WithAgentUserIdentity("agent-identity-id", "user@tenant.com"));
});

Llamada a API personalizadas con IDownstreamApi

Use IDownstreamApi para llamar a sus propias API protegidas con cualquiera de los tres patrones de autenticación:

using Microsoft.Identity.Abstractions;

IDownstreamApi downstreamApi =
    serviceProvider.GetRequiredService<IDownstreamApi>();

// Standard daemon
var result = await downstreamApi.GetForAppAsync<ApiResponse>(
    serviceName: "MyApi",
    options => options.RelativePath = "api/data");

// With agent identity
var result = await downstreamApi.GetForAppAsync<ApiResponse>(
    serviceName: "MyApi",
    options =>
    {
        options.RelativePath = "api/data";
        options.WithAgentIdentity("agent-identity-id");
    });

// Agent user identity
var result = await downstreamApi.GetForUserAsync<ApiResponse>(
    serviceName: "MyApi",
    options =>
    {
        options.RelativePath = "api/data";
        options.WithAgentUserIdentity("agent-identity-id", "user@tenant.com");
    });

Realización de llamadas HTTP manuales

Use IAuthorizationHeaderProvider directamente cuando necesite control total sobre las solicitudes HTTP:

using Microsoft.Identity.Abstractions;

IAuthorizationHeaderProvider authProvider =
    serviceProvider.GetRequiredService<IAuthorizationHeaderProvider>();

HttpClient httpClient = new HttpClient();

// Standard daemon
string authHeader = await authProvider.CreateAuthorizationHeaderForAppAsync(
    "https://graph.microsoft.com/.default");

httpClient.DefaultRequestHeaders.Add("Authorization", authHeader);
var response = await httpClient.GetStringAsync("https://graph.microsoft.com/v1.0/users");

// With agent identity
var options = new AuthorizationHeaderProviderOptions()
    .WithAgentIdentity("agent-identity-id");

authHeader = await authProvider.CreateAuthorizationHeaderForAppAsync(
    "https://graph.microsoft.com/.default",
    options);

// Agent user identity
var userOptions = new AuthorizationHeaderProviderOptions()
    .WithAgentUserIdentity("agent-identity-id", "user@tenant.com");

authHeader = await authProvider.CreateAuthorizationHeaderForUserAsync(
    new[] { "https://graph.microsoft.com/.default" },
    userOptions);

Configuración del almacenamiento en caché de tokens

Elija una estrategia de almacenamiento en caché basada en su entorno.

Desarrollo: caché en memoria

Use el almacenamiento en caché en memoria para el desarrollo y las pruebas locales:

services.AddInMemoryTokenCaches();

Producción: caché distribuida

En el caso de producción, use una caché distribuida para conservar los tokens entre los reinicios de la aplicación y las instancias de escalabilidad horizontal.

SQL Server

Almacenar tokens en una tabla de SQL Server:

services.AddDistributedSqlServerCache(options =>
{
    options.ConnectionString = configuration["ConnectionStrings:TokenCache"];
    options.SchemaName = "dbo";
    options.TableName = "TokenCache";
});
services.AddDistributedTokenCaches();

Redis

Use Redis para el almacenamiento en caché de tokens distribuidos de alto rendimiento:

services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = configuration["Redis:ConnectionString"];
    options.InstanceName = "TokenCache_";
});
services.AddDistributedTokenCaches();

Cosmos DB

Use Cosmos DB para el almacenamiento en caché de tokens distribuidos globalmente:

services.AddCosmosDbTokenCaches(options =>
{
    options.CosmosDbConnectionString = configuration["CosmosDb:ConnectionString"];
    options.DatabaseId = "TokenCache";
    options.ContainerId = "Tokens";
});

Más información:Configuración de caché de tokens


Exploración de ejemplos de Azure

Microsoft proporciona ejemplos que muestran patrones de aplicaciones de servicio.

Repositorio de ejemplo

active-directory-dotnetcore-daemon-v2

Este repositorio contiene varios escenarios:

Ejemplo Descripción Vínculo
1-Call-MSGraph Demonio básico llamando a Microsoft Graph con credenciales de cliente Ver ejemplo
2-Call-OwnApi Demonio que llama a su propia API web protegida Ver ejemplo
3-Using-KeyVault Daemon usando Azure Key Vault para el almacenamiento de certificados Ver ejemplo
4-Multiinquilino Aplicación de servicio multiarrendatario Ver ejemplo
5-Call-MSGraph-ManagedIdentity Demonio usando Identidad Administrada en Azure Ver ejemplo

Comparación de patrones de ejemplo con patrones de producción

Los ejemplos de Azure usan TokenAcquirerFactory.GetDefaultInstance() para simplificar: el enfoque recomendado para simple aplicaciones de consola, prototipos y pruebas. En esta guía se muestran ambos patrones:

Patrón TokenAcquirerFactory (muestras de Azure):

// Simple, perfect for prototypes and tests
var tokenAcquirerFactory = TokenAcquirerFactory.GetDefaultInstance();
tokenAcquirerFactory.Services.AddDownstreamApi("MyApi", ...);
var serviceProvider = tokenAcquirerFactory.Build();

Patrón completo de ServiceCollection (Aplicaciones de producción):

// More control, testable, follows DI best practices
var services = new ServiceCollection();
services.AddTokenAcquisition(true); // true = singleton
services.Configure<MicrosoftIdentityApplicationOptions>(...);
var serviceProvider = services.BuildServiceProvider();

Cuándo usar qué:

  • Uso TokenAcquirerFactory para: Aplicaciones de consola, prototipos rápidos, pruebas unitarias, servicios de demonio simples
  • Use ServiceCollection para: Aplicaciones de producción, integración ASP.NET Core, escenarios complejos de di, servicios en segundo plano con IHostedService

Ambos enfoques son totalmente compatibles y listos para producción. Elija en función de las necesidades de integración y complejidad de la aplicación.


Solución de errores comunes

AADSTS700016: no se encontró la aplicación

Causa: Aplicación no válida ClientId o no registrada en el arrendatario.

Solución: Verifique que el ClientId en su configuración coincida con el registro de la aplicación de Microsoft Entra.

AADSTS7000215: secreto de cliente no válido

Causa: El secreto de cliente es incorrecto, ha expirado o no está configurado.

Solution:

  • Verifique que el secreto en Azure portal coincide con su configuración.
  • Comprobación de la fecha de expiración del secreto
  • Considere la posibilidad de usar certificados para producción

AADSTS700027: la aserción de cliente contiene una firma no válida

Causa: No se encuentra el certificado, ha expirado o no se puede acceder a la clave privada.

Solution:

  • Comprobación de que el certificado está instalado en el almacén de certificados correcto
  • Comprobar que el nombre distintivo del certificado coincide con la configuración
  • Asegúrese de que la aplicación tiene permiso para leer la clave privada
  • Consulte la Guía de configuración de certificados.

AADSTS650052: la aplicación necesita acceso a un servicio

Causa: Faltan los permisos de API necesarios o falta el consentimiento del administrador.

Solution:

  1. Vaya al portal de Azure → Registros de aplicaciones → Su aplicación → Permisos de la API
  2. Agregar permisos necesarios (por ejemplo, User.Read.All para Microsoft Graph)
  3. Haga clic en el botón "Conceder consentimiento del administrador"

Errores de identidad del agente

AADSTS50105: el usuario que ha iniciado sesión no está asignado a un rol

Causa: La identidad del agente no está configurada correctamente o no está asignada a la aplicación.

Solution:

  • Compruebe que la identidad del agente existe en Microsoft Entra ID
  • Asegúrese de que la identidad del agente esté vinculada a la aplicación.
  • Verificar que la identidad del agente tiene los permisos necesarios.

Tokens adquiridos pero con permisos incorrectos

Causa: Usar la identidad de usuario del agente pero solicitar permisos de aplicación, o viceversa.

Solution:

  • Para tokens de solo aplicación: uso CreateAuthorizationHeaderForAppAsync con WithAgentIdentity
  • Para tokens delegados: use CreateAuthorizationHeaderForUserAsync con WithAgentUserIdentity
  • Asegúrese de que los permisos de API coinciden con el tipo de token (aplicación frente a delegado)

Problemas de almacenamiento en caché de tokens

Problema: Los tokens no se almacenan en caché, lo que fuerza una nueva adquisición cada vez.

Solution:

  • Para la identidad de usuario del agente: reutilización de la misma ClaimsPrincipal instancia entre llamadas
  • Comprobación de la conexión de caché distribuida (si usa Redis/SQL)
  • Habilite el registro de depuración para ver las operaciones de caché

Diagnóstico detallado:Guía de registro y diagnóstico