Skip to content

Commit 55b01e3

Browse files
Add meaningful error message when identity configuration is missing (#3637)
* Add meaningful error message when identity configuration is missing (fixes #2921) Co-authored-by: neha-bhargava <[email protected]> * Remove non-existent aka.ms link from error message Co-authored-by: neha-bhargava <[email protected]> * Re-add aka.ms link to error message Co-authored-by: neha-bhargava <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: neha-bhargava <[email protected]>
1 parent 88ec4ff commit 55b01e3

File tree

10 files changed

+80
-1
lines changed

10 files changed

+80
-1
lines changed

src/Microsoft.Identity.Web.TokenAcquisition/IDWebErrorMessage.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ internal static class IDWebErrorMessage
6666
"StoreLocation must be one of 'CurrentUser', 'LocalMachine'. " +
6767
"StoreName must be empty or one of '{0}'. ";
6868

69+
// Configuration Validation IDW10700+ = "IDW10700+:"
70+
public const string MissingIdentityConfiguration = "IDW10708: The identity configuration is incomplete. Provide either 'Instance' and 'TenantId', or 'Authority', or enable 'ManagedIdentity' in the configuration. Check your configuration keys for typos (e.g., trailing spaces). See https://aka.ms/ms-id-web/configuration. ";
71+
6972
// Obsolete messages IDW10800 = "IDW10800:"
7073
public const string AadIssuerValidatorGetIssuerValidatorIsObsolete = "IDW10800: Use MicrosoftIdentityIssuerValidatorFactory.GetAadIssuerValidator. See https://aka.ms/ms-id-web/1.2.0. ";
7174
public const string InitializeAsyncIsObsolete = "IDW10801: Use Initialize instead. See https://aka.ms/ms-id-web/1.9.0. ";

src/Microsoft.Identity.Web.TokenAcquisition/PublicAPI/net10.0/InternalAPI.Unshipped.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ static Microsoft.Identity.Web.MergedOptions.ParseAuthorityIfNecessary(Microsoft.
99
static Microsoft.Identity.Web.MergedOptionsLogging.AuthorityIgnored(Microsoft.Extensions.Logging.ILogger! logger, string! authority, string! instance, string! tenantId) -> void
1010
static Microsoft.Identity.Web.TokenAcquisition.MergeExtraQueryParameters(Microsoft.Identity.Web.MergedOptions! mergedOptions, Microsoft.Identity.Web.TokenAcquisitionOptions? tokenAcquisitionOptions) -> System.Collections.Generic.Dictionary<string!, (string! value, bool includeInCacheKey)>?
1111
static readonly Microsoft.Identity.Web.LoggingEventId.AuthorityIgnored -> Microsoft.Extensions.Logging.EventId
12+
const Microsoft.Identity.Web.IDWebErrorMessage.MissingIdentityConfiguration = "IDW10708: The identity configuration is incomplete. Provide either 'Instance' and 'TenantId', or 'Authority', or enable 'ManagedIdentity' in the configuration. Check your configuration keys for typos (e.g., trailing spaces). See https://aka.ms/ms-id-web/configuration. " -> string!

src/Microsoft.Identity.Web.TokenAcquisition/PublicAPI/net462/InternalAPI.Unshipped.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ static Microsoft.Identity.Web.MergedOptions.ParseAuthorityIfNecessary(Microsoft.
77
static Microsoft.Identity.Web.MergedOptionsLogging.AuthorityIgnored(Microsoft.Extensions.Logging.ILogger! logger, string! authority, string! instance, string! tenantId) -> void
88
static Microsoft.Identity.Web.TokenAcquisition.MergeExtraQueryParameters(Microsoft.Identity.Web.MergedOptions! mergedOptions, Microsoft.Identity.Web.TokenAcquisitionOptions? tokenAcquisitionOptions) -> System.Collections.Generic.Dictionary<string!, (string! value, bool includeInCacheKey)>?
99
static readonly Microsoft.Identity.Web.LoggingEventId.AuthorityIgnored -> Microsoft.Extensions.Logging.EventId
10+
const Microsoft.Identity.Web.IDWebErrorMessage.MissingIdentityConfiguration = "IDW10708: The identity configuration is incomplete. Provide either 'Instance' and 'TenantId', or 'Authority', or enable 'ManagedIdentity' in the configuration. Check your configuration keys for typos (e.g., trailing spaces). See https://aka.ms/ms-id-web/configuration. " -> string!

src/Microsoft.Identity.Web.TokenAcquisition/PublicAPI/net472/InternalAPI.Unshipped.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ static Microsoft.Identity.Web.MergedOptions.ParseAuthorityIfNecessary(Microsoft.
77
static Microsoft.Identity.Web.MergedOptionsLogging.AuthorityIgnored(Microsoft.Extensions.Logging.ILogger! logger, string! authority, string! instance, string! tenantId) -> void
88
static Microsoft.Identity.Web.TokenAcquisition.MergeExtraQueryParameters(Microsoft.Identity.Web.MergedOptions! mergedOptions, Microsoft.Identity.Web.TokenAcquisitionOptions? tokenAcquisitionOptions) -> System.Collections.Generic.Dictionary<string!, (string! value, bool includeInCacheKey)>?
99
static readonly Microsoft.Identity.Web.LoggingEventId.AuthorityIgnored -> Microsoft.Extensions.Logging.EventId
10+
const Microsoft.Identity.Web.IDWebErrorMessage.MissingIdentityConfiguration = "IDW10708: The identity configuration is incomplete. Provide either 'Instance' and 'TenantId', or 'Authority', or enable 'ManagedIdentity' in the configuration. Check your configuration keys for typos (e.g., trailing spaces). See https://aka.ms/ms-id-web/configuration. " -> string!

src/Microsoft.Identity.Web.TokenAcquisition/PublicAPI/net8.0/InternalAPI.Unshipped.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ static Microsoft.Identity.Web.MergedOptions.ParseAuthorityIfNecessary(Microsoft.
77
static Microsoft.Identity.Web.MergedOptionsLogging.AuthorityIgnored(Microsoft.Extensions.Logging.ILogger! logger, string! authority, string! instance, string! tenantId) -> void
88
static Microsoft.Identity.Web.TokenAcquisition.MergeExtraQueryParameters(Microsoft.Identity.Web.MergedOptions! mergedOptions, Microsoft.Identity.Web.TokenAcquisitionOptions? tokenAcquisitionOptions) -> System.Collections.Generic.Dictionary<string!, (string! value, bool includeInCacheKey)>?
99
static readonly Microsoft.Identity.Web.LoggingEventId.AuthorityIgnored -> Microsoft.Extensions.Logging.EventId
10+
const Microsoft.Identity.Web.IDWebErrorMessage.MissingIdentityConfiguration = "IDW10708: The identity configuration is incomplete. Provide either 'Instance' and 'TenantId', or 'Authority', or enable 'ManagedIdentity' in the configuration. Check your configuration keys for typos (e.g., trailing spaces). See https://aka.ms/ms-id-web/configuration. " -> string!

src/Microsoft.Identity.Web.TokenAcquisition/PublicAPI/net9.0/InternalAPI.Unshipped.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ static Microsoft.Identity.Web.MergedOptions.ParseAuthorityIfNecessary(Microsoft.
77
static Microsoft.Identity.Web.MergedOptionsLogging.AuthorityIgnored(Microsoft.Extensions.Logging.ILogger! logger, string! authority, string! instance, string! tenantId) -> void
88
static Microsoft.Identity.Web.TokenAcquisition.MergeExtraQueryParameters(Microsoft.Identity.Web.MergedOptions! mergedOptions, Microsoft.Identity.Web.TokenAcquisitionOptions? tokenAcquisitionOptions) -> System.Collections.Generic.Dictionary<string!, (string! value, bool includeInCacheKey)>?
99
static readonly Microsoft.Identity.Web.LoggingEventId.AuthorityIgnored -> Microsoft.Extensions.Logging.EventId
10+
const Microsoft.Identity.Web.IDWebErrorMessage.MissingIdentityConfiguration = "IDW10708: The identity configuration is incomplete. Provide either 'Instance' and 'TenantId', or 'Authority', or enable 'ManagedIdentity' in the configuration. Check your configuration keys for typos (e.g., trailing spaces). See https://aka.ms/ms-id-web/configuration. " -> string!

src/Microsoft.Identity.Web.TokenAcquisition/PublicAPI/netstandard2.0/InternalAPI.Unshipped.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ static Microsoft.Identity.Web.MergedOptions.ParseAuthorityIfNecessary(Microsoft.
77
static Microsoft.Identity.Web.MergedOptionsLogging.AuthorityIgnored(Microsoft.Extensions.Logging.ILogger! logger, string! authority, string! instance, string! tenantId) -> void
88
static Microsoft.Identity.Web.TokenAcquisition.MergeExtraQueryParameters(Microsoft.Identity.Web.MergedOptions! mergedOptions, Microsoft.Identity.Web.TokenAcquisitionOptions? tokenAcquisitionOptions) -> System.Collections.Generic.Dictionary<string!, (string! value, bool includeInCacheKey)>?
99
static readonly Microsoft.Identity.Web.LoggingEventId.AuthorityIgnored -> Microsoft.Extensions.Logging.EventId
10+
const Microsoft.Identity.Web.IDWebErrorMessage.MissingIdentityConfiguration = "IDW10708: The identity configuration is incomplete. Provide either 'Instance' and 'TenantId', or 'Authority', or enable 'ManagedIdentity' in the configuration. Check your configuration keys for typos (e.g., trailing spaces). See https://aka.ms/ms-id-web/configuration. " -> string!

src/Microsoft.Identity.Web.TokenAcquisition/TokenAcquisition.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -594,7 +594,8 @@ public async Task<AuthenticationResult> GetAuthenticationResultForAppAsync(
594594
}
595595

596596
// Apply tenant override only for AAD authorities and only if non-empty
597-
if (!mergedOptions.Instance.Contains(Constants.CiamAuthoritySuffix
597+
if (!string.IsNullOrEmpty(mergedOptions.Instance) &&
598+
!mergedOptions.Instance.Contains(Constants.CiamAuthoritySuffix
598599
#if NET6_0_OR_GREATER
599600
, StringComparison.OrdinalIgnoreCase
600601
#endif
@@ -938,6 +939,16 @@ private async Task<IConfidentialClientApplication> BuildConfidentialClientApplic
938939
{
939940
mergedOptions.PrepareAuthorityInstanceForMsal();
940941

942+
// Validate that we have enough configuration to build an authority
943+
// When PreserveAuthority is true, we use Authority directly, so PreparedInstance is not required
944+
// When IsB2C is true, we still need PreparedInstance
945+
if (!mergedOptions.PreserveAuthority &&
946+
string.IsNullOrEmpty(mergedOptions.PreparedInstance) &&
947+
string.IsNullOrEmpty(mergedOptions.Authority))
948+
{
949+
throw new ArgumentException(IDWebErrorMessage.MissingIdentityConfiguration);
950+
}
951+
941952
try
942953
{
943954
ConfidentialClientApplicationBuilder builder = ConfidentialClientApplicationBuilder

tests/Microsoft.Identity.Web.Test/MergedOptionsTests.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,5 +292,26 @@ public void PrepareAuthorityInstanceForMsal_DoesNotParseAuthority_WhenInstanceAn
292292
Assert.Equal("organizations", options.TenantId); // TenantId remains unchanged
293293
Assert.Equal("https://login.microsoftonline.us/", options.PreparedInstance); // PreparedInstance based on original Instance
294294
}
295+
296+
[Fact]
297+
public void PrepareAuthorityInstanceForMsal_LeavesNullPreparedInstance_WhenNoConfigurationProvided()
298+
{
299+
// This test verifies the scenario from issue #2921 where a misconfigured key
300+
// (e.g., "ManagedIdentity " with trailing space instead of "ManagedIdentity")
301+
// results in null Instance and null Authority, which should leave PreparedInstance as null
302+
303+
// Arrange
304+
var options = new MergedOptions();
305+
// Simulating the scenario where configuration keys have typos and don't bind correctly
306+
307+
// Act
308+
options.PrepareAuthorityInstanceForMsal();
309+
310+
// Assert
311+
Assert.Null(options.Instance);
312+
Assert.Null(options.TenantId);
313+
Assert.Null(options.Authority);
314+
Assert.Null(options.PreparedInstance);
315+
}
295316
}
296317
}

tests/Microsoft.Identity.Web.Test/TokenAcquisitionTests.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,5 +150,43 @@ private TokenAcquirerFactory InitTokenAcquirerFactory()
150150

151151
return tokenAcquirerFactory;
152152
}
153+
154+
/// <summary>
155+
/// Tests that when identity configuration is missing (simulating a misconfigured key like "ManagedIdentity " with trailing space),
156+
/// a meaningful ArgumentException is thrown instead of a NullReferenceException.
157+
/// This addresses issue #2921.
158+
/// </summary>
159+
[Fact]
160+
public async Task GetAuthenticationResultForAppAsync_ThrowsMeaningfulError_WhenConfigurationIsMissing()
161+
{
162+
// Arrange - Create a factory with missing identity configuration
163+
TokenAcquirerFactoryTesting.ResetTokenAcquirerFactoryInTest();
164+
TokenAcquirerFactory tokenAcquirerFactory = TokenAcquirerFactory.GetDefaultInstance();
165+
tokenAcquirerFactory.Services.Configure<MicrosoftIdentityApplicationOptions>(options =>
166+
{
167+
// Intentionally NOT setting Instance, TenantId, or Authority
168+
// This simulates the scenario where configuration keys have typos
169+
// (e.g., "ManagedIdentity " instead of "ManagedIdentity")
170+
options.ClientId = "test-client-id";
171+
options.ClientCredentials = [new CredentialDescription()
172+
{
173+
SourceType = CredentialSource.ClientSecret,
174+
ClientSecret = "someSecret"
175+
}];
176+
});
177+
178+
tokenAcquirerFactory.Services.AddSingleton<IMsalHttpClientFactory, MockHttpClientFactory>();
179+
180+
IServiceProvider serviceProvider = tokenAcquirerFactory.Build();
181+
IAuthorizationHeaderProvider authorizationHeaderProvider = serviceProvider.GetRequiredService<IAuthorizationHeaderProvider>();
182+
183+
// Act & Assert - Should throw ArgumentException with meaningful message
184+
var exception = await Assert.ThrowsAsync<ArgumentException>(async () =>
185+
await authorizationHeaderProvider.CreateAuthorizationHeaderForAppAsync(
186+
"https://graph.microsoft.com/.default",
187+
new AuthorizationHeaderProviderOptions()));
188+
189+
Assert.StartsWith(IDWebErrorMessage.MissingIdentityConfiguration, exception.Message, System.StringComparison.Ordinal);
190+
}
153191
}
154192
}

0 commit comments

Comments
 (0)