microsoft/teams.net

Public

mirrored fromhttps://github.com/microsoft/teams.netAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v2.0.8

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

core/src/Microsoft.Teams.Core/Hosting/BotConfig.cs

143lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4using Microsoft.Extensions.Configuration;
5using Microsoft.Extensions.DependencyInjection;
6using Microsoft.Extensions.Logging;
7
8namespace Microsoft.Teams.Core.Hosting;
9
10/// <summary>
11/// Configuration model for bot authentication credentials, sourced from a
12/// Microsoft.Identity.Web compatible configuration section (e.g. "AzureAd").
13/// </summary>
14public sealed class BotConfig
15{
16 internal const string DefaultSectionName = "AzureAd";
17
18 internal const string BotFrameworkSectionName = "BotFramework";
19
20 internal const string DefaultOpenIdMetadataUrl = "https://login.botframework.com/v1/.well-known/openid-configuration";
21
22 internal const string DefaultEntraInstance = "https://login.microsoftonline.com/";
23
24 internal const string DefaultBotTokenIssuer = "https://api.botframework.com";
25
26 /// <summary>
27 /// Gets or sets the Azure AD tenant ID.
28 /// </summary>
29 public string TenantId { get; set; } = string.Empty;
30
31 /// <summary>
32 /// Gets or sets the application (client) ID from Azure AD app registration.
33 /// </summary>
34 public string ClientId { get; set; } = string.Empty;
35
36 /// <summary>
37 /// Gets or sets the configuration section name used to resolve this BotConfig.
38 /// Also used as the MSAL named-options key and the JWT auth scheme name.
39 /// </summary>
40 public string SectionName { get; set; } = DefaultSectionName;
41
42 /// <summary>
43 /// Gets or sets the Bot Framework OpenID metadata URL used to fetch signing keys
44 /// for validating inbound Bot Framework tokens. For sovereign clouds, set
45 /// <c>BotFramework:OpenIdMetadataUrl</c> in configuration, e.g.
46 /// <c>"https://login.botframework.azure.us/v1/.well-known/openid-configuration"</c> for USGov.
47 /// Defaults to the public-cloud endpoint when not configured.
48 /// </summary>
49 public string OpenIdMetadataUrl { get; set; } = DefaultOpenIdMetadataUrl;
50
51 /// <summary>
52 /// Gets or sets the Entra login instance used when validating Entra-issued tokens.
53 /// For sovereign clouds, set <c>{SectionName}:Instance</c> in configuration
54 /// (the standard Microsoft.Identity.Web key), e.g.
55 /// <c>"https://login.microsoftonline.us/"</c> for USGov.
56 /// Defaults to the public-cloud instance when not configured.
57 /// </summary>
58 public string EntraInstance { get; set; } = DefaultEntraInstance;
59
60 /// <summary>
61 /// Gets or sets the expected Bot Framework token issuer used to validate inbound
62 /// Bot Framework tokens. For sovereign clouds, set <c>BotFramework:BotTokenIssuer</c>
63 /// in configuration, e.g. <c>"https://api.botframework.us"</c> for USGov.
64 /// Defaults to the public-cloud issuer when not configured.
65 /// </summary>
66 public string BotTokenIssuer { get; set; } = DefaultBotTokenIssuer;
67
68 internal IConfigurationSection? MsalConfigurationSection { get; set; }
69
70 /// <summary>
71 /// Gets a value indicating whether this configuration uses User-Assigned Managed Identity (UMI) for authentication.
72 /// Returns true when no ClientCredentials are configured in the section.
73 /// </summary>
74 internal bool IsUserAssignedManagedIdentity =>
75 MsalConfigurationSection is not null &&
76 !MsalConfigurationSection.GetSection("ClientCredentials").GetChildren().Any();
77
78 /// <summary>
79 /// Resolves a BotConfig from a service collection by extracting configuration and logger.
80 /// </summary>
81 /// <param name="services">The service collection containing IConfiguration and ILoggerFactory registrations.</param>
82 /// <param name="sectionName">The configuration section name. Defaults to "AzureAd".</param>
83 /// <returns>A BotConfig populated from the section, or an empty BotConfig if no ClientId is configured.</returns>
84 public static BotConfig Resolve(IServiceCollection services, string sectionName = DefaultSectionName)
85 {
86 ArgumentNullException.ThrowIfNull(services);
87
88 IConfiguration? configuration = AddBotApplicationExtensions.ResolveFromServicesPreHost<IConfiguration>(services);
89
90 if (configuration is null)
91 {
92 throw new InvalidOperationException(
93 "IConfiguration must be registered in the service collection before calling BotConfig.Resolve. " +
94 "Ensure AddConfiguration() or WebApplication.CreateBuilder() has been called.");
95 }
96
97 IConfigurationSection section = configuration.GetSection(sectionName);
98 IConfigurationSection botFrameworkSection = configuration.GetSection(BotFrameworkSectionName);
99 BotConfig config = new()
100 {
101 TenantId = section["TenantId"] ?? string.Empty,
102 ClientId = section["ClientId"] ?? string.Empty,
103 EntraInstance = ResolveAbsoluteUri(section, "Instance", DefaultEntraInstance),
104 OpenIdMetadataUrl = ResolveAbsoluteUri(botFrameworkSection, "OpenIdMetadataUrl", DefaultOpenIdMetadataUrl),
105 BotTokenIssuer = ResolveAbsoluteUri(botFrameworkSection, "BotTokenIssuer", DefaultBotTokenIssuer),
106 MsalConfigurationSection = section,
107 SectionName = sectionName
108 };
109
110 AddBotApplicationExtensions.LogFromServices(services, l =>
111 {
112 if (!string.IsNullOrEmpty(config.ClientId))
113 _logUsingSectionConfig(l, sectionName, null);
114 else
115 _logNoConfigFound(l, null);
116 }, typeof(BotConfig));
117
118 return config;
119 }
120
121 private static string ResolveAbsoluteUri(IConfigurationSection section, string key, string defaultValue)
122 {
123 ArgumentNullException.ThrowIfNull(section);
124
125 string? value = section[key];
126 if (value is null)
127 {
128 return defaultValue;
129 }
130 if (!Uri.TryCreate(value, UriKind.Absolute, out _))
131 {
132 throw new InvalidOperationException(
133 $"Configuration value '{section.Key}:{key}' is not a valid absolute URI: '{value}'.");
134 }
135 return value;
136 }
137
138 private static readonly Action<ILogger, string, Exception?> _logUsingSectionConfig =
139 LoggerMessage.Define<string>(LogLevel.Debug, new(3), "Resolved bot configuration from '{SectionName}' configuration section");
140 private static readonly Action<ILogger, Exception?> _logNoConfigFound =
141 LoggerMessage.Define(LogLevel.Warning, new(4), "No bot configuration found in configuration.");
142
143}
144