Condividi tramite


Applicazioni daemon e identità dell'agente con Microsoft. Identity.Web

Questa guida illustra come creare applicazioni daemon, servizi in background e agenti autonomi usando Microsoft. Identity.Web. Queste applicazioni vengono eseguite senza interazione dell'utente ed eseguono l'autenticazione usando l'identità dell'applicazione (credenziali client) o le identità dell'agente.

Informazioni generali

Microsoft. Identity.Web supporta tre tipi di applicazioni non interattive:

Scenario Tipo di autenticazione Tipo di token Caso d'uso
Standard Daemon Credenziali client (segreto/certificato) Token di accesso solo per app Servizi in background, processi pianificati, elaborazione dati
Agente autonomo Identità agente con credenziali client Token di accesso solo per app per l'agente Agenti Copilot, servizi autonomi che agiscono per conto di un'identità di agente. (in genere in un'API Web protetta)
Identità utente agente Identità utente agente Identità dell'utente agente con credenziali del client Servizi autonomi che agiscono per conto di un'identità utente di Agent. (in genere in un'API Web protetta)

Avvio rapido

Prerequisiti

  • .NET 8.0 o versione successiva
  • Microsoft Entra registrazione dell'app con credenziali del client (segreto della chiave client o certificato)
  • Per scenari relativi agli agenti: identità dell'agente configurate nel tenant Microsoft Entra

Installazione

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

Due approcci di configurazione

Microsoft. Identity.Web offre due modi per configurare le applicazioni daemon:

Ideale per: Prototipi rapidi, app console, test e semplici servizi daemon.

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();

Vantaggi:

  • Codice boilerplate minimo
  • Carica automaticamente appsettings.json
  • Perfetto per scenari semplici
  • Inizializzazione in una riga
  • Non adatto per i test in esecuzione in parallelo (singleton)

Ideale per: Applicazioni di produzione, scenari complessi, inserimento delle dipendenze, testability.

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();

Vantaggi:

  • Controllo completo sui provider di configurazione
  • Maggiore testabilità con l'iniezione di costruttore
  • Si integra con il modello di hosting di ASP.NET Core
  • Supporta scenari complessi (più schemi di autenticazione)
  • Architettura pronta per la produzione
  • Supporta l'esecuzione parallela dei test (provider di servizi isolati per ciascun test)

Annotazioni

Il parametro true in AddTokenAcquisition(true) indica che il servizio viene registrato come singleton (istanza singola per la durata dell'app). Usare false per la durata con ambito nelle applicazioni Web.

Raccomandazione: Iniziare con TokenAcquirerFactory per i prototipi e i test a thread singolo. Eseguire la migrazione al modello completo ServiceCollection durante la compilazione di applicazioni di produzione o l'esecuzione di test paralleli.


Applicazioni daemon standard

Le applicazioni daemon standard eseguono l'autenticazione usando credenziali client (segreto client o certificato) e ottengono token di accesso solo app per chiamare le API.

Configurazione

appsettings.json:

{
  "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: Imposta il tuo appsettings.json per copiare nella directory di output:

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

Questa operazione viene eseguita automaticamente nelle applicazioni ASP.NET Core, ma non per le app daemon (o OWIN)

Program.cs:

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();

Chiamata di Microsoft Graph

DaemonWorker.cs:

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 di IAuthorizationHeaderProvider

Per un maggiore controllo sulle chiamate HTTP:

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;
    }
}

Vedere anche Calling downstream API per informazioni su tutti i modi in cui Microsoft Identity Web propone di chiamare LE API downstream.


Agenti autonomi (identità agente)

Gli agenti autonomi usano le identità degli agenti per ottenere token esclusivi per l'app. Ciò è utile per gli scenari Copilot, i servizi autonomi.

Microsoft consiglia agli agenti di chiamare le API downstream nelle API Web protette anche se questi agenti autonomi acquisiranno un token dell'app

Configurazione

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();

Acquisizione di token con identità dell'agente

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);
    });
});

Esempio completo dell'agente autonomo

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>();
    }
}

Identità utente agente

L'identità utente agente consente agli agenti di agire per conto di un utente agente con autorizzazioni delegate. Questo è per gli agenti che hanno la propria cassetta postale, ecc ...

Prerequisiti

  • Progetto agent registrato in Microsoft Entra ID
  • Identità dell'agente creata e collegata all'applicazione agente
  • Identità utente agente associata all'identità dell'agente

Configurazione

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();

Acquisizione di token utente con l'identità dell'agente

Per nome utente (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));
});

Id oggetto utente

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));
});

Memorizzazione nella cache dei token con ClaimsPrincipal

Per prestazioni migliori, memorizzare nella cache i token utente usando ClaimsPrincipal:

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

Sovrascrittura del tenant

Per gli scenari multi-tenant, eseguire l'override del tenant in fase di esecuzione:

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";
    });
});

Esempio completo di identità utente dell'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());
    }
}

Configurazione del servizio

Creare un metodo di estensione riutilizzabile per una configurazione coerente:

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();
    }
}

Utilizzo

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

var serviceProvider = services.ConfigureServicesForAgentIdentities(configuration);

Chiamata di API

Microsoft Graph

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"));
});

API personalizzate con IDownstreamApi

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");
    });

Chiamate HTTP manuali

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);

Memorizzazione nella cache dei token

Sviluppo: cache in memoria

services.AddInMemoryTokenCaches();

Produzione: cache distribuita

SQL Server

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

Redis

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

Cosmos DB

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

Altre informazioni:Configurazione della cache dei token


esempi di Azure

Microsoft fornisce esempi completi che illustrano i modelli di app daemon:

Repository di esempio

active-directory-dotnetcore-daemon-v2

Questo repository contiene più scenari:

Esempio Descrizione Link
1-Call-MSGraph Daemon di base che chiama Microsoft Graph con credenziali client Visualizza esempio
2-Call-OwnApi Daemon che chiama la tua API Web protetta Visualizza esempio
3-Using-KeyVault Daemon con Azure Key Vault per l'archiviazione dei certificati Visualizza esempio
4-Multi-Tenant Applicazione daemon multi-tenant Visualizza esempio
5-Call-MSGraph-ManagedIdentity Daemon che usa l'identità gestita in Azure Visualizza esempio

Differenze principali rispetto agli esempi

Gli esempi di Azure usano TokenAcquirerFactory.GetDefaultInstance() per semplicità: questo è l'approccio consigliato per applicazioni console semplici, prototipi e test. Questa guida illustra entrambi i modelli:

Modello TokenAcquirerFactory (Azure Samples):

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

Full ServiceCollection Pattern (per applicazioni di produzione):

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

Quando usare quale:

  • Utilizzare TokenAcquirerFactory per: app console, prototipi rapidi, unit test, servizi daemon semplici
  • Usare ServiceCollection per: applicazioni di produzione, integrazione ASP.NET Core, scenari di inserimento delle dipendenze complessi, servizi in background con IHostedService

Entrambi gli approcci sono completamente supportati e pronti per la produzione. Scegliere in base alle esigenze di complessità e integrazione dell'applicazione.


Risoluzione dei problemi

AADSTS700016: applicazione non trovata

Causa: Non valido ClientId o l'applicazione non è registrata nel tenant.

Solution: Verificare che il ClientId nella configurazione corrisponda alla registrazione dell'app Microsoft Entra.

AADSTS7000215: Client Secret non valido

Causa: Il segreto client non è corretto, è scaduto o non è configurato.

Soluzione:

  • Verificare che il segreto nel portale di Azure corrisponda alla configurazione
  • Controllare la data di scadenza del segreto
  • Prendere in considerazione l'uso dei certificati per la produzione

AADSTS700027: l'asserzione client contiene una firma non valida

Causa: Certificato non trovato, scaduto o chiave privata non accessibile.

Soluzione:

  • Verificare che il certificato sia installato nell'archivio certificati corretto
  • Controllare che il nome distinto del certificato corrisponda alla configurazione
  • Verificare che l'applicazione disponga dell'autorizzazione per leggere la chiave privata
  • Vedere La Guida alla configurazione dei certificati

AADSTS650052: l'app deve accedere a un servizio

Causa: Autorizzazioni API necessarie non concesse o consenso amministratore mancante.

Soluzione:

  1. Passare al portale di Azure → Registrazioni app → Autorizzazioni API per l'app →
  2. Aggiungere le autorizzazioni necessarie (ad esempio, User.Read.All per Microsoft Graph)
  3. Fare clic sul pulsante "Concedi consenso amministratore"

Errori dell'identità dell'agente

AADSTS50105: l'utente connesso non è assegnato a un ruolo

Causa: Identità dell'agente non configurata correttamente o non assegnata all'applicazione.

Soluzione:

  • Verificare che l'identità dell'agente esista in Microsoft Entra ID
  • Verificare che l'identità dell'agente sia collegata all'applicazione
  • Verificare che l'identità dell'agente disponga delle autorizzazioni necessarie

Token acquisiti ma con autorizzazioni errate

Causa: Uso dell'identità utente dell'agente ma richiesta di autorizzazioni dell'app o viceversa.

Soluzione:

  • Per token riservati ad app: usare CreateAuthorizationHeaderForAppAsync con WithAgentIdentity
  • Per i token delegati: usare CreateAuthorizationHeaderForUserAsync con WithAgentUserIdentity
  • Verificare che le autorizzazioni API corrispondano al tipo di token (applicazione e delegata)

Problemi di memorizzazione nella cache dei token

Problema: I token non vengono memorizzati nella cache, forzando ogni volta la nuova acquisizione.

Soluzione:

  • Per l'identità utente dell'agente: riutilizzare la stessa ClaimsPrincipal istanza tra le chiamate
  • Verificare la connessione alla cache distribuita (se si usa Redis/SQL)
  • Abilitare la registrazione di debug per visualizzare le operazioni della cache

Diagnostica dettagliata:Guida alla registrazione e alla diagnostica