Создание демон-приложений и идентификаций агента с помощью Microsoft.Identity.Web

В этой статье вы создадите приложения управляющей программы, фоновые службы и автономные агенты с помощью Microsoft. Identity.Web. Эти приложения выполняются без взаимодействия пользователя и проверки подлинности с помощью удостоверения приложения (учетные данные клиента) или удостоверений агента.

Общие сведения о поддерживаемых сценариях

Microsoft. Identity.Web поддерживает три типа неинтерактивных приложений:

Сценарий Тип проверки подлинности Тип токена Вариант использования
Стандартная управляющая программа Учетные данные клиента (секрет или сертификат) Маркер доступа только для приложений Фоновые службы, запланированные задания, обработка данных
Автономный агент Удостоверение агента с учетными данными клиента Маркер доступа только для приложения для агента Агенты Copilot, автономные службы, действующие от имени агентской идентичности. (Обычно в защищенном веб-API)
Удостоверение пользователя агента Удостоверение пользователя агента Удостоверение пользователя агента с учетными данными клиента Автономные службы, действующие от имени идентификатора пользователя агента. (Обычно в защищенном веб-API)

Начало работы

Необходимые условия

Прежде чем начать, убедитесь, что у вас есть:

  • .NET 8.0 или более поздней версии
  • Регистрация приложения в Microsoft Entra с использованием клиентских учетных данных (клиентский секрет или сертификат)
  • Для сценариев агента: удостоверения агента, настроенные в клиенте Microsoft Entra

Установка пакетов

Добавьте необходимые пакеты NuGet в проект:

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

Выбор подхода к конфигурации

Microsoft. Identity.Web предоставляет два способа настройки приложений управляющей программы:

Лучше всего: Быстрые прототипы, консольные приложения, тестирование и простые службы управляющей программы.

Следующий код создает TokenAcquirerFactory, настраивает подчиненные API и Microsoft Graph и вызывает API 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();

Преимущества.

  • Минимальный стандартный код
  • Автоматическая загрузка appsettings.json
  • Идеально подходит для простых сценариев
  • Однострочная инициализация

Недостатки:

  • Не подходит для тестов, выполняемых параллельно (одноэлементный)

Лучше всего: Рабочие приложения, сложные сценарии, внедрение зависимостей, возможность тестирования.

Следующий код использует общий хост .NET для настройки аутентификации, получения токенов, кэширования и фоновой службы.

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

Преимущества.

  • Полный контроль над поставщиками конфигураций
  • Улучшена возможность тестирования с помощью внедрения конструктора
  • Интегрируется с моделью размещения ASP.NET Core
  • Поддерживает сложные сценарии (несколько схем проверки подлинности)
  • Архитектура, готовая к работе
  • Поддерживает параллельное выполнение тестов (изолированный поставщик услуг для каждого теста)

Замечание

Параметр true в AddTokenAcquisition(true) означает, что служба регистрируется как синглтон (один экземпляр для времени существования приложения). Используйте false для ограниченного срока службы в веб-приложениях.

Рекомендации: Начните с TokenAcquirerFactory прототипов и однопоточных тестов. Миграция на полный ServiceCollection шаблон при создании рабочих приложений или выполнении параллельных тестов.


Настройка стандартных приложений управляющей программы

Стандартные приложения управляющей программы проходят проверку подлинности с помощью учетных данных клиента (секрет клиента или сертификат) и получают маркеры доступа только для приложений для вызова API.

Настроить параметры аутентификации

Добавьте следующую конфигурацию в файл 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
    ]
  }
}

Важно: Установите ваш appsettings.json на копирование в выходной каталог. Добавьте следующее в ваш файл .csproj.

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

ASP.NET Core приложения автоматически копируют этот файл, но демон-приложения (и приложения OWIN) этого не делают.

Настройка конфигурации службы

Следующий код Program.cs регистрирует опции Microsoft Identity, получение токенов, кэширование и хостинг фоновой службы.

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

Подключение к Microsoft Graph

Следующий класс DaemonWorker.cs использует Graph SDK для получения списка пользователей на повторяющейся основе.

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

Используйте IAuthorizationHeaderProvider

Для получения большего контроля над HTTP-вызовами используйте IAuthorizationHeaderProvider для создания заголовков авторизации вручную:

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

См. также Использование внешних API чтобы узнать обо всех способах вызова зависимых API через Microsoft Identity Web.


Настройка автономных агентов (идентичность агента)

Автономные агенты используют идентичности агента для получения токенов, предназначенных только для приложений. Этот шаблон полезен для сценариев Copilot и автономных служб.

Замечание

Microsoft рекомендует, чтобы агенты, вызывающие вторичные API, делали это из защищенных веб-API, даже если агенты получают токен приложения.

Настройка служб агента

Следующий код настраивает аутентификацию, получение токенов и поддержку идентификации агента с помощью конфигурации в памяти.

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

Получение токенов с использованием идентификации агента

После настройки служб агента получите токены с помощью IAuthorizationHeaderProvider или Microsoft Graph SDK.

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

Просмотрите полный пример автономного агента

Следующий класс оборачивает процесс получения токена идентификации агента и вызовы API Graph в повторно используемую службу.

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

Настройка идентификации пользователя агента

Удостоверение пользователя агента позволяет агентам действовать от имени пользователя агента с делегированными разрешениями. Используйте этот шаблон для агентов, которым требуются собственные почтовые ящики или другие ресурсы с областью действия пользователя.

Необходимые условия

Чтобы использовать удостоверение пользователя агента, вам потребуется:

  • Схема агента, зарегистрированная в Microsoft Entra ID
  • Идентификатор агента создан и привязан к приложению агента
  • Идентификатор пользователя агента, связанный с идентификатором агента

Настройка пользовательских служб агента

Следующий код настраивает идентификацию приложения агента с сертификатом аутентификации и регистрирует необходимые службы.

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

Получение токенов пользователей, используя идентификацию агента

Вы можете определить целевого пользователя по имени пользователя или идентификатору объекта.

По имени пользователя (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));
});

По идентификатору объекта пользователя

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

Кэширование токенов с ClaimsPrincipal

Для повышения производительности кэшируйте токены пользователей, передавая экземпляр ClaimsPrincipal. Первый вызов заполняет субъект и uidutid утверждения; последующие вызовы повторно используют кэшированный токен:

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

Переопределить арендатора

Для сценариев с несколькими арендаторами можно переопределить арендатора во время выполнения. Это полезно, когда приложение настроено с использованием "common", но должно быть нацелено на конкретного арендатора.

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

Просмотрите полный пример удостоверения пользователя агента

Следующий класс предоставляет методы для получения профилей пользователей и заголовков авторизации с помощью удостоверения пользователя агента:

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

Создание конфигурации повторно используемых служб

Определение метода расширения

Создайте повторно используемый метод расширения, чтобы инкапсулировать конфигурацию удостоверения агента в приложении:

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

Использование метода расширения

Вызовите метод расширения для настройки служб в одной строке:

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

var serviceProvider = services.ConfigureServicesForAgentIdentities(configuration);

Вызов API-интерфейсов

В этом разделе показано, как вызывать API с помощью каждого из трех шаблонов проверки подлинности.

Подключение к Microsoft Graph

В следующих примерах демонстрируется вызов 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 с помощью IDownstreamApi

Используйте IDownstreamApi для вызова собственных защищенных API с любым из трех шаблонов проверки подлинности:

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

Выполнение вызовов HTTP вручную

Используйте IAuthorizationHeaderProvider непосредственно, если требуется полный контроль над 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);

Настроить кэширование токенов

Выберите стратегию кэширования в зависимости от среды.

Разработка: кэш в памяти

Используйте кэширование в памяти для локальной разработки и тестирования:

services.AddInMemoryTokenCaches();

Продакшн: распределенный кэш

Для рабочей среды используйте распределенный кэш для сохранения маркеров в перезапусках приложения и экземплярах горизонтального масштабирования.

SQL Server

Храните токены в таблице 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

Используйте Cosmos DB для кэширования глобально распределённых токенов.

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

Дополнительные сведения:Конфигурация кэша токенов


Изучение примеров Azure

Microsoft предоставляет примеры, демонстрирующие шаблоны приложений-демонов.

Пример репозитория

active-directory-dotnetcore-daemon-v2

Этот репозиторий содержит несколько сценариев:

Образец Описание Ссылка
1-Call-MSGraph Базовый демон, обращающийся к Microsoft Graph с учетными данными клиента Посмотреть образец
2-Call-OwnApi Демон вызывает ваши собственные защищенные веб-API Посмотреть образец
3-Using-KeyVault Демон, использующий Azure Key Vault для хранения сертификатов Посмотреть образец
4-Multi-Tenant Мультитенантное приложение-демон Посмотреть образец
5-Call-MSGraph-ManagedIdentity Демон, использующий управляемое удостоверение в Azure Посмотреть образец

Сравнение примеров шаблонов с рабочими шаблонами

Примеры Azure используют TokenAcquirerFactory.GetDefaultInstance() для простоты — как рекомендованный подход для простых консольных приложений, прототипов и тестов. В этом руководстве показаны оба шаблона:

Шаблон TokenAcquirerFactory (примеры Azure):

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

Шаблон Full ServiceCollection (приложения для производства):

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

Когда следует использовать:

  • Использовать TokenAcquirerFactory для: консольные приложения, быстрые прототипы, модульные тесты, простые службы управляющей программы
  • Use ServiceCollection для: производственных приложений, интеграции ASP.NET Core, сложных сценариев DI, фоновых служб с IHostedService

Оба подхода полностью поддерживаются и готовы к работе. Выберите в зависимости от сложности и интеграции приложения.


Устранение распространенных ошибок

AADSTS700016: приложение не найдено

Причина: Некорректный ClientId или приложение не зарегистрировано в клиенте.

Solution: Убедитесь, что ClientId в конфигурации соответствует регистрации приложения Microsoft Entra.

AADSTS7000215: недопустимый секрет клиента

Причина: Клиентский секрет неверен, истек или не настроен.

Solution:

  • Убедитесь, что секрет на портале Azure соответствует вашей конфигурации.
  • Проверка даты окончания срока действия секрета
  • Рассмотрите возможность использования сертификатов для рабочей среды

AADSTS700027: утверждение клиента содержит недопустимую подпись

Причина: Сертификат не найден, истек срок действия или закрытый ключ недоступен.

Solution:

  • Проверьте, установлен ли сертификат в правильном хранилище сертификатов.
  • Проверьте, что уникальное имя сертификата соответствует конфигурации
  • Убедитесь, что приложение имеет разрешение на чтение закрытого ключа
  • См . руководство по настройке сертификата

AADSTS650052: приложению требуется доступ к службе

Причина: Не предоставлены необходимые разрешения API или отсутствует согласие администратора.

Solution:

  1. Перейдите на портал Azure → Регистрация приложений → ваше приложение → API-разрешения
  2. Добавление необходимых разрешений (например, User.Read.All для Microsoft Graph)
  3. Нажмите кнопку "Предоставить согласие администратора"

Ошибки идентификации агента

AADSTS50105: пользователь, вошедший в систему, не назначен на роль

Причина: Идентификация агента неправильно настроена или не назначена приложению.

Solution:

  • Проверка наличия удостоверения агента в Microsoft Entra ID
  • Убедитесь, что удостоверение личности агента связано с вашим приложением
  • Убедитесь, что удостоверение агента имеет необходимые разрешения

Маркеры, приобретенные, но с неправильными разрешениями

Причина: Использование удостоверения агента, но запрос разрешений приложения (или наоборот).

Solution:

  • Для маркеров, доступных только для приложений: использование CreateAuthorizationHeaderForAppAsync с WithAgentIdentity
  • Для делегированных маркеров: используйте CreateAuthorizationHeaderForUserAsync с WithAgentUserIdentity
  • Убедитесь, что разрешения API соответствуют типу токена (приложение или делегировано)

Проблемы с кэшированием токенов

Проблема: Токены не кэшируются, что вынуждает совершать новое приобретение каждый раз.

Solution:

  • Для идентификации пользователя агента: используйте один и тот же экземпляр ClaimsPrincipal для всех вызовов
  • Проверка подключения распределенного кэша (при использовании Redis/SQL)
  • Включение ведения журнала отладки для просмотра операций кэша

Подробное руководство по диагностике:ведение журнала и диагностика