이 문서에서는 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은 디먼 애플리케이션을 구성하는 두 가지 방법을 제공합니다.
옵션 1: TokenAcquirerFactory(간단한 시나리오에 권장)
최적 대상: 빠른 프로토타입, 콘솔 앱, 테스트 및 간단한 디먼 서비스.
다음 코드는 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로드됩니다 - 간단한 시나리오에 적합
- 한 줄 초기화
단점:
- 병렬로 실행되는 테스트에 적합하지 않음(싱글톤)
옵션 2: 전체 ServiceCollection, 프로덕션에 권장
최적 대상: 프로덕션 애플리케이션, 복잡한 시나리오, 종속성 주입, 테스트 가능성.
다음 코드는 .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 인스턴스를 전달하여 사용자 토큰을 캐시하십시오. 첫 번째 호출은 주체와 uid 및 utid 클레임을 채웁니다. 다음 호출은 캐시된 토큰을 다시 사용합니다.
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:
- Azure 포털로 이동합니다 → 앱 등록 → 당신의 앱 → API 권한
- 필요한 권한 추가(예: Microsoft Graph
User.Read.All) - "관리자 동의 부여" 단추를 클릭합니다.
에이전트 ID 오류
AADSTS50105: 로그인한 사용자가 역할에 할당되지 않음
원인: 에이전트 ID가 제대로 구성되지 않았거나 애플리케이션에 할당되지 않았습니다.
Solution:
- Microsoft Entra ID에 에이전트 ID가 존재하는지 확인
- 에이전트 ID가 애플리케이션에 연결되어 있는지 확인
- 에이전트 ID에 필요한 권한이 있는지 확인합니다.
획득되었지만 잘못된 권한이 있는 토큰
원인: 에이전트 사용자 ID를 사용하지만 앱 권한을 요청하거나 그 반대의 경우도 마찬가지입니다.
Solution:
-
앱 전용 토큰의 경우:
CreateAuthorizationHeaderForAppAsync와 함께WithAgentIdentity를 사용합니다. - API 권한이 토큰 유형과 일치하는지 확인합니다(애플리케이션 및 위임됨).
토큰 캐싱 문제
문제: 토큰은 캐시되지 않으므로 매번 새 토큰을 강제로 획득합니다.
Solution:
- 에이전트 사용자 ID의 경우: 호출에서 동일한
ClaimsPrincipal인스턴스 다시 사용 - 분산 캐시 연결 확인(Redis/SQL을 사용하는 경우)
- 디버그 로깅을 사용하도록 설정하여 캐시 작업 확인
자세한 진단:로깅 및 진단 가이드
관련 콘텐츠
- 웹 API에서 다운스트림 API 호출 - OBO 패턴
- MSAL.NET 프레임워크 가이드 - .NET Framework에 대한 토큰 캐시 및 인증서 구성
- Certificate 구성 - 키 보관소, 저장소, 파일 또는 Base64에서 인증서 로드
- 토큰 캐시 구성 - 프로덕션 캐싱 전략
- 로깅 및 진단 - 토큰 획득 문제 해결
- 사용자 지정 가이드 - 고급 구성 패턴
- Microsoft ID 플랫폼 디먼 앱 설명서
- Azure 샘플: 데몬 애플리케이션
- Microsoft. Identity.Web NuGet 패키지
- Microsoft. Identity.Abstractions API 참조