Microsoft.Identity.Web을 사용하여 데몬 애플리케이션 및 에이전트 ID를 빌드합니다.

이 문서에서는 Microsoft.Identity.Web을 사용하여 데몬 애플리케이션, 백그라운드 서비스 및 자율 에이전트를 빌드합니다. 이러한 애플리케이션은 사용자 상호 작용 없이 실행되며 애플리케이션 ID (클라이언트 자격 증명) 또는 에이전트 ID를 사용하여 인증합니다.

지원되는 시나리오 이해

Microsoft. Identity.Web은 다음 세 가지 유형의 비대화형 애플리케이션을 지원합니다.

시나리오 인증 유형 토큰 유형 사용 사례
표준 디먼 클라이언트 자격 증명(비밀/인증서) 앱 전용 액세스 토큰 백그라운드 서비스, 예약된 작업, 데이터 처리
자율 에이전트 클라이언트 자격 증명을 사용하는 에이전트 ID 에이전트에 대한 앱 전용 액세스 토큰 Copilot 에이전트는 에이전트 ID를 대신하여 작동하는 자율 서비스입니다. (일반적으로 보호된 Web API에서)
에이전트 사용자 ID 에이전트 사용자 ID 클라이언트 자격 증명을 사용하는 에이전트 사용자 신원 확인 에이전트 사용자 ID를 대신하여 작동하는 자율 서비스입니다. (일반적으로 보호된 Web API에서)

시작

사전 요구 사항

시작하기 전에 다음을 확인합니다.

  • .NET 8.0 이상
  • client 자격 증명(클라이언트 암호 또는 인증서)을 사용하여 Microsoft Entra에 앱을 등록합니다.
  • 에이전트 시나리오의 경우: Microsoft Entra 테넌트에 구성된 에이전트 ID

패키지 설치

프로젝트에 필요한 NuGet 패키지를 추가합니다.

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

구성 방법 선택

Microsoft. Identity.Web은 디먼 애플리케이션을 구성하는 두 가지 방법을 제공합니다.

최적 대상: 빠른 프로토타입, 콘솔 앱, 테스트 및 간단한 디먼 서비스.

다음 코드는 TokenAcquirerFactory 만들고, 다운스트림 API 및 Microsoft Graph 구성하고, Graph API 호출합니다.

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 ID 옵션, 토큰 획득, 캐싱 및 호스트된 백그라운드 서비스를 등록합니다.

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 호출을 참조하여 Microsoft Identity Web이 다운스트림 API를 호출하는 모든 방법에 대해 알아봅니다.


자율 에이전트 구성(에이전트 ID)

자율 에이전트는 에이전트 ID 를 사용하여 앱 전용 토큰을 가져옵니다. 이 패턴은 Copilot 시나리오 및 자율 서비스에 유용합니다.

메모

Microsoft 에이전트가 앱 토큰을 획득하는 경우에도 다운스트림 API를 호출하는 에이전트가 보호된 웹 API 내에서 이 작업을 수행하는 것이 좋습니다.

에이전트 서비스 구성

다음 코드는 메모리 내 구성을 사용하여 인증, 토큰 획득 및 에이전트 ID 지원을 설정합니다.

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

에이전트 ID를 사용하여 토큰 획득

에이전트 서비스를 구성한 후 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);
    });
});

완전한 자율 에이전트 예제 검토

다음 클래스는 에이전트 ID 토큰 획득 및 Graph API 호출을 재사용 가능한 서비스로 래핑합니다.

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

에이전트 사용자 ID 구성

에이전트 사용자 ID를 사용하면 에이전트가 위임된 권한을 가진 에이전트 사용자를 대신하여 작업할 수 있습니다. 자체 사서함 또는 다른 사용자 범위 리소스가 필요한 에이전트에 이 패턴을 사용합니다.

사전 요구 사항

에이전트 사용자 ID를 사용하려면 다음이 필요합니다.

  • 에이전트 청사진이 Microsoft Entra ID에 등록됨
  • 에이전트 ID가 만들어지고 에이전트 애플리케이션에 연결됨
  • 에이전트 아이디와 연결된 에이전트 사용자 아이디

에이전트 사용자 서비스 구성

다음 코드는 인증서 자격 증명을 사용하여 에이전트 애플리케이션 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();

에이전트 ID를 사용하여 사용자 토큰 획득

UPN 또는 개체 ID로 대상 사용자를 식별할 수 있습니다.

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별

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

전체 에이전트 사용자 ID 예제 검토

다음 클래스는 에이전트 사용자 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());
    }
}

재사용 가능한 서비스 구성 만들기

확장 메서드 정의

애플리케이션 전체에서 에이전트 ID 구성을 캡슐화하는 재사용 가능한 확장 메서드를 만듭니다.

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 호출

다음 예제에서는 표준 디먼, 자치 에이전트 및 에이전트 사용자 ID로 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"));
});

IDownstreamApi를 사용하여 사용자 지정 API 호출

다음 세 가지 인증 패턴 중에서 사용자 고유의 보호된 API를 호출하는 데 사용합니다 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");
    });

수동 HTTP 호출

HTTP 요청을 완전히 제어해야 하는 경우 직접 사용합니다 IAuthorizationHeaderProvider .

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

이 리포지토리에는 다음과 같은 여러 시나리오가 포함되어 있습니다.

샘플 설명 Link
1-Call-MSGraph 클라이언트 자격 증명을 사용하여 Microsoft Graph 호출하는 기본 디먼 샘플 보기
2-Call-OwnApi 사용자 고유의 보호된 웹 API를 호출하는 디먼 샘플 보기
3-Using-KeyVault 인증서 스토리지에 Azure Key Vault 사용하는 디먼 샘플 보기
4-멀티 테넌트 다중 테넌트 디먼 애플리케이션 샘플 보기
5-Call-MSGraph-ManagedIdentity Azure 관리 ID를 사용하는 디먼 샘플 보기

샘플 패턴과 프로덕션 패턴 비교

Azure 샘플은 간단한 콘솔 앱, 프로토타입 및 테스트를 위해 TokenAcquirerFactory.GetDefaultInstance()를 사용하여 단순성을 제공하는 권장 방식입니다. 이 가이드에서는 두 패턴을 모두 보여 줍니다.

TokenAcquirerFactory 패턴(Azure의 샘플):

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

서비스 컬렉션 전체 패턴(프로덕션 앱):

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

사용 시기:

  • TokenAcquirerFactory 사용 용: 콘솔 앱, 빠른 프로토타입, 단위 테스트, 간단한 데몬 서비스
  • ServiceCollection 사용

두 방법 모두 완전히 지원되며 프로덕션 준비가 완료되었습니다. 애플리케이션의 복잡성 및 통합 요구 사항에 따라 선택합니다.


일반적인 오류 해결 방법

AADSTS700016: 애플리케이션을 찾을 수 없음

원인: 유효하지 ClientId 않거나 애플리케이션이 테넌트에 등록되지 않았습니다.

Solution: 구성의 ClientId Microsoft Entra 앱 등록과 일치하는지 확인합니다.

AADSTS7000215: 잘못된 클라이언트 암호

원인: 클라이언트 암호가 잘못되었거나, 만료되었거나, 구성되지 않았습니다.

Solution:

  • Azure 포털의 비밀이 구성과 일치하는지 확인합니다.
  • 비밀 만료 날짜 확인
  • 프로덕션에 인증서를 사용하는 것이 좋습니다.

AADSTS700027: 클라이언트 어설션에 잘못된 서명이 포함되어 있습니다.

원인: 인증서를 찾을 수 없거나 만료되었거나 프라이빗 키에 액세스할 수 없습니다.

Solution:

  • 인증서가 올바른 인증서 저장소에 설치되어 있는지 확인
  • 인증서 고유 이름이 구성과 일치하는지 확인
  • 애플리케이션에 프라이빗 키를 읽을 수 있는 권한이 있는지 확인
  • 인증서 구성 가이드 참조

AADSTS650052: 앱이 서비스에 액세스해야 합니다.

원인: 필수 API 권한이 부여되지 않았거나 관리자 동의가 누락되었습니다.

Solution:

  1. Azure 포털로 이동합니다 → 앱 등록 → 당신의 앱 → API 권한
  2. 필요한 권한 추가(예: Microsoft Graph User.Read.All)
  3. "관리자 동의 부여" 단추를 클릭합니다.

에이전트 ID 오류

AADSTS50105: 로그인한 사용자가 역할에 할당되지 않음

원인: 에이전트 ID가 제대로 구성되지 않았거나 애플리케이션에 할당되지 않았습니다.

Solution:

  • Microsoft Entra ID에 에이전트 ID가 존재하는지 확인
  • 에이전트 ID가 애플리케이션에 연결되어 있는지 확인
  • 에이전트 ID에 필요한 권한이 있는지 확인합니다.

획득되었지만 잘못된 권한이 있는 토큰

원인: 에이전트 사용자 ID를 사용하지만 앱 권한을 요청하거나 그 반대의 경우도 마찬가지입니다.

Solution:

  • 앱 전용 토큰의 경우:CreateAuthorizationHeaderForAppAsync와 함께 WithAgentIdentity를 사용합니다.
  • API 권한이 토큰 유형과 일치하는지 확인합니다(애플리케이션 및 위임됨).

토큰 캐싱 문제

문제: 토큰은 캐시되지 않으므로 매번 새 토큰을 강제로 획득합니다.

Solution:

  • 에이전트 사용자 ID의 경우: 호출에서 동일한 ClaimsPrincipal 인스턴스 다시 사용
  • 분산 캐시 연결 확인(Redis/SQL을 사용하는 경우)
  • 디버그 로깅을 사용하도록 설정하여 캐시 작업 확인

자세한 진단:로깅 및 진단 가이드