// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Net.Http.Headers; using Microsoft.Extensions.Options; using Microsoft.Identity.Abstractions; using Microsoft.Teams.Core.Schema; namespace PABot { internal class PACustomAuthHandler( IAuthorizationHeaderProvider authorizationHeaderProvider, IRoutedTokenAcquisitionService routedTokenService, ILogger logger, string botScope, string? agenticScope = null, IOptions? managedIdentityOptions = null) : DelegatingHandler { private readonly IAuthorizationHeaderProvider _authorizationHeaderProvider = authorizationHeaderProvider ?? throw new ArgumentNullException(nameof(authorizationHeaderProvider)); private readonly IRoutedTokenAcquisitionService _routedTokenService = routedTokenService ?? throw new ArgumentNullException(nameof(routedTokenService)); private readonly ILogger _logger = logger ?? throw new ArgumentNullException(nameof(logger)); private readonly string _botScope = botScope ?? throw new ArgumentNullException(nameof(botScope)); private readonly string _agenticScope = agenticScope ?? botScope; // Default to bot scope if not specified private readonly IOptions? _managedIdentityOptions = managedIdentityOptions; /// /// Key used to store the agentic identity in HttpRequestMessage options. /// When set, agentic application credentials will be used instead of bot credentials. /// public static readonly HttpRequestOptionsKey AgenticIdentityKey = new("AgenticIdentity"); /// protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { request.Options.TryGetValue(AgenticIdentityKey, out AgenticIdentity? agenticIdentity); string token = await GetAuthorizationHeaderAsync(agenticIdentity, cancellationToken).ConfigureAwait(false); string tokenValue = token.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase) ? token["Bearer ".Length..] : token; request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokenValue); return await base.SendAsync(request, cancellationToken).ConfigureAwait(false); } /// /// Gets an authorization header for API calls. /// Routes to either bot credentials or agentic application credentials based on the presence of AgenticIdentity. /// /// Optional agentic identity. When provided, agentic application credentials are used. /// Cancellation token. /// The authorization header value. private async Task GetAuthorizationHeaderAsync(AgenticIdentity? agenticIdentity, CancellationToken cancellationToken) { // If agentic identity is provided, use agentic application credentials with agentic scope if (agenticIdentity is not null && !string.IsNullOrEmpty(agenticIdentity.AgenticAppId) && !string.IsNullOrEmpty(agenticIdentity.AgenticUserId)) { _logger.LogInformation("Acquiring token using agentic credentials for scope '{Scope}' with AppId '{AppId}' and UserId '{UserId}'.", _agenticScope, agenticIdentity.AgenticAppId, agenticIdentity.AgenticUserId); return await _routedTokenService.AcquireTokenForAgenticAsync(agenticIdentity, _agenticScope, cancellationToken).ConfigureAwait(false); } // Otherwise, use bot credentials with bot scope _logger.LogInformation("Acquiring token using bot credentials for scope: {Scope}", _botScope); return await _routedTokenService.AcquireTokenForBotAsync(_botScope, cancellationToken).ConfigureAwait(false); } } }