Skip to content

Commit 9103068

Browse files
committed
Fix for 3000 - improve logs for credential loading
1 parent c8ba0ce commit 9103068

File tree

14 files changed

+533
-56
lines changed

14 files changed

+533
-56
lines changed

src/Microsoft.Identity.Web.Certificate/DefaultCredentialsLoader.cs

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
33

4+
using System;
45
using System.Collections.Concurrent;
56
using System.Collections.Generic;
67
using System.Threading;
@@ -10,11 +11,34 @@
1011

1112
namespace Microsoft.Identity.Web
1213
{
14+
1315
/// <summary>
1416
/// Default credentials loader.
1517
/// </summary>
1618
public class DefaultCredentialsLoader : ICredentialsLoader
1719
{
20+
/// <summary>
21+
/// Logging infrastructure
22+
/// </summary>
23+
private static class Logger
24+
{
25+
public static void CredentialLoadingFailure(ILogger? logger, CredentialDescription cd, Exception? ex)
26+
{
27+
if (logger != null && logger.IsEnabled(LogLevel.Information))
28+
{
29+
__CredentialLoadingFailure(logger, cd.Id, cd.CredentialType.ToString(), cd.Skip, ex);
30+
}
31+
}
32+
33+
private static readonly Action<ILogger, string, string, bool, Exception?> __CredentialLoadingFailure =
34+
LoggerMessage.Define<string, string, bool>(
35+
LogLevel.Information,
36+
new EventId(
37+
7,
38+
nameof(CredentialLoadingFailure)),
39+
"Failed to load credential {id} from source {sourceType}. Will it be skipped in the future ? {skip}.");
40+
}
41+
1842
ILogger<DefaultCredentialsLoader>? _logger;
1943
private readonly ConcurrentDictionary<string, SemaphoreSlim> _loadingSemaphores = new ConcurrentDictionary<string, SemaphoreSlim>();
2044

@@ -32,7 +56,7 @@ public DefaultCredentialsLoader(ILogger<DefaultCredentialsLoader>? logger)
3256
{ CredentialSource.StoreWithThumbprint, new StoreWithThumbprintCertificateLoader() },
3357
{ CredentialSource.StoreWithDistinguishedName, new StoreWithDistinguishedNameCertificateLoader() },
3458
{ CredentialSource.Base64Encoded, new Base64EncodedCertificateLoader() },
35-
{ CredentialSource.SignedAssertionFromManagedIdentity, new SignedAssertionFromManagedIdentityCredentialLoader() },
59+
{ CredentialSource.SignedAssertionFromManagedIdentity, new SignedAssertionFromManagedIdentityCredentialLoader(_logger) },
3660
{ CredentialSource.SignedAssertionFilePath, new SignedAssertionFilePathCredentialsLoader(_logger) }
3761
};
3862
}
@@ -51,7 +75,8 @@ public DefaultCredentialsLoader() : this(null)
5175
public IDictionary<CredentialSource, ICredentialSourceLoader> CredentialSourceLoaders { get; }
5276

5377
/// <inheritdoc/>
54-
/// Load the credentials from the description, if needed.
78+
/// Load the credentials from the description, if needed.
79+
/// Important: Ignores SKIP flag, propagates exceptions.
5580
public async Task LoadCredentialsIfNeededAsync(CredentialDescription credentialDescription, CredentialSourceLoaderParameters? parameters = null)
5681
{
5782
_ = Throws.IfNull(credentialDescription);
@@ -69,7 +94,17 @@ public async Task LoadCredentialsIfNeededAsync(CredentialDescription credentialD
6994
if (credentialDescription.CachedValue == null)
7095
{
7196
if (CredentialSourceLoaders.TryGetValue(credentialDescription.SourceType, out ICredentialSourceLoader? loader))
72-
await loader.LoadIfNeededAsync(credentialDescription, parameters);
97+
{
98+
try
99+
{
100+
await loader.LoadIfNeededAsync(credentialDescription, parameters);
101+
}
102+
catch (Exception ex)
103+
{
104+
Logger.CredentialLoadingFailure(_logger, credentialDescription, ex);
105+
throw;
106+
}
107+
}
73108
}
74109
}
75110
finally
@@ -81,11 +116,18 @@ public async Task LoadCredentialsIfNeededAsync(CredentialDescription credentialD
81116
}
82117

83118
/// <inheritdoc/>
84-
public async Task<CredentialDescription?> LoadFirstValidCredentialsAsync(IEnumerable<CredentialDescription> credentialDescriptions, CredentialSourceLoaderParameters? parameters = null)
119+
/// Loads first valid credential which is not marked as Skipped.
120+
public async Task<CredentialDescription?> LoadFirstValidCredentialsAsync(
121+
IEnumerable<CredentialDescription> credentialDescriptions,
122+
CredentialSourceLoaderParameters? parameters = null)
85123
{
86124
foreach (var credentialDescription in credentialDescriptions)
87125
{
88126
await LoadCredentialsIfNeededAsync(credentialDescription, parameters);
127+
128+
// TODO: Exceptions which occur in trying various credentials are logged and ignored.
129+
// is this really intended behavior? This is different from WithClientCredentialsAsync
130+
89131
if (!credentialDescription.Skip)
90132
{
91133
return credentialDescription;
@@ -107,6 +149,5 @@ public void ResetCredentials(IEnumerable<CredentialDescription> credentialDescri
107149
}
108150
}
109151
}
110-
111152
}
112153
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using Microsoft.Extensions.Logging;
5+
6+
namespace Microsoft.Identity.Web
7+
{
8+
/// <summary>
9+
/// EventIds for Logging.
10+
/// </summary>
11+
internal static class LoggingEventId
12+
{
13+
#pragma warning disable IDE1006 // Naming Styles
14+
// SessionCacheProvider EventIds 200+
15+
public static readonly EventId SessionCache = new EventId(200, "SessionCache");
16+
public static readonly EventId SessionCacheKeyNotFound = new EventId(201, "SessionCacheKeyNotFound");
17+
18+
// TokenAcquisition EventIds 300+
19+
public static readonly EventId TokenAcquisitionError = new EventId(300, "TokenAcquisitionError");
20+
public static readonly EventId TokenAcquisitionMsalAuthenticationResultTime = new EventId(301, "TokenAcquisitionMsalAuthenticationResultTime");
21+
22+
// ConfidentialClientApplicationBuilderExtension EventIds 400+
23+
public static readonly EventId NotUsingManagedIdentity = new EventId(400, "NotUsingManagedIdentity");
24+
public static readonly EventId UsingManagedIdentity = new EventId(401, "UsingManagedIdentity");
25+
public static readonly EventId UsingPodIdentityFile = new EventId(402, "UsingPodIdentityFile");
26+
public static readonly EventId UsingCertThumbprint = new EventId(403, "UsingCertThumbprint");
27+
public static readonly EventId UsingSignedAssertionFromVault = new EventId(404, "UsingSignedAssertionFromVault");
28+
public static readonly EventId CredentialLoadAttempt = new EventId(405, "CredentialLoadAttempt");
29+
public static readonly EventId CredentialLoadAttemptFailed = new EventId(406, "CredentialLoadAttemptFailed");
30+
31+
#pragma warning restore IDE1006 // Naming Styles
32+
}
33+
}

src/Microsoft.Identity.Web.Certificate/SignedAssertionFilePathCredentialsLoader.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,14 @@ public async Task LoadIfNeededAsync(CredentialDescription credentialDescription,
3636
{
3737
// Given that managed identity can be not available locally, we need to try to get a
3838
// signed assertion, and if it fails, move to the next credentials
39-
_= await signedAssertion!.GetSignedAssertionAsync(null);
39+
_ = await signedAssertion!.GetSignedAssertionAsync(null);
4040
credentialDescription.CachedValue = signedAssertion;
4141
}
4242
catch (Exception)
4343
{
4444
credentialDescription.Skip = true;
45-
}
45+
throw;
46+
}
4647
}
4748
}
4849
}

src/Microsoft.Identity.Web.Certificate/SignedAssertionFromManagedIdentityCredentialLoader.cs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,21 @@
44
using System.Threading;
55
using System.Threading.Tasks;
66
using Azure.Identity;
7+
using Microsoft.Extensions.Logging;
78
using Microsoft.Identity.Abstractions;
9+
using Microsoft.Identity.Client;
810

911
namespace Microsoft.Identity.Web
1012
{
1113
internal class SignedAssertionFromManagedIdentityCredentialLoader : ICredentialSourceLoader
1214
{
15+
private ILogger<DefaultCredentialsLoader>? _logger;
16+
17+
public SignedAssertionFromManagedIdentityCredentialLoader(ILogger<DefaultCredentialsLoader>? logger)
18+
{
19+
_logger = logger;
20+
}
21+
1322
public CredentialSource CredentialSource => CredentialSource.SignedAssertionFromManagedIdentity;
1423

1524
public async Task LoadIfNeededAsync(CredentialDescription credentialDescription, CredentialSourceLoaderParameters? credentialSourceLoaderParameters)
@@ -19,16 +28,19 @@ public async Task LoadIfNeededAsync(CredentialDescription credentialDescription,
1928
ManagedIdentityClientAssertion? managedIdentityClientAssertion = credentialDescription.CachedValue as ManagedIdentityClientAssertion;
2029
if (credentialDescription.CachedValue == null)
2130
{
22-
managedIdentityClientAssertion = new ManagedIdentityClientAssertion(credentialDescription.ManagedIdentityClientId, credentialDescription.TokenExchangeUrl);
31+
managedIdentityClientAssertion = new ManagedIdentityClientAssertion(
32+
credentialDescription.ManagedIdentityClientId,
33+
credentialDescription.TokenExchangeUrl,
34+
_logger);
2335
}
2436
try
2537
{
2638
// Given that managed identity can be not available locally, we need to try to get a
2739
// signed assertion, and if it fails, move to the next credentials
28-
_= await managedIdentityClientAssertion!.GetSignedAssertionAsync(null);
40+
_ = await managedIdentityClientAssertion!.GetSignedAssertionAsync(null);
2941
credentialDescription.CachedValue = managedIdentityClientAssertion;
3042
}
31-
catch (AuthenticationFailedException)
43+
catch (MsalServiceException)
3244
{
3345
credentialDescription.Skip = true;
3446
throw;

src/Microsoft.Identity.Web.Certificateless/AzureIdentityForKubernetesClientAssertion.Logger.cs

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,28 @@ namespace Microsoft.Identity.Web
88
{
99
public partial class AzureIdentityForKubernetesClientAssertion
1010
{
11-
/*
12-
// High performance logger messages (before generation).
13-
#pragma warning disable SYSLIB1009 // Logging methods must be static
14-
[LoggerMessage(EventId = 1, Level = LogLevel.Information, Message = "SignedAssertionFileDiskPath not provided. Falling back to the content of the AZURE_FEDERATED_TOKEN_FILE environment variable. ")]
15-
partial void SignedAssertionFileDiskPathNotProvided(ILogger logger);
11+
/*
12+
// High performance logger messages (before generation).
13+
#pragma warning disable SYSLIB1009 // Logging methods must be static
14+
[LoggerMessage(EventId = 1, Level = LogLevel.Information, Message = "SignedAssertionFileDiskPath not provided. Falling back to the content of the AZURE_FEDERATED_TOKEN_FILE environment variable. ")]
15+
partial void SignedAssertionFileDiskPathNotProvided(ILogger logger);
1616
17-
[LoggerMessage(EventId = 2, Level = LogLevel.Information, Message = "The `{environmentVariableName}` environment variable not provided. ")]
18-
partial void SignedAssertionEnvironmentVariableNotProvided(ILogger logger, string environmentVariableName);
17+
[LoggerMessage(EventId = 2, Level = LogLevel.Information, Message = "The `{environmentVariableName}` environment variable not provided. ")]
18+
partial void SignedAssertionEnvironmentVariableNotProvided(ILogger logger, string environmentVariableName);
1919
20-
[LoggerMessage(EventId = 3, Level = LogLevel.Error, Message = "The environment variable AZURE_FEDERATED_TOKEN_FILE or AZURE_ACCESS_TOKEN_FILE or the 'SignedAssertionFileDiskPath' must be set to the path of the file containing the signed assertion. ")]
21-
partial void NoSignedAssertionParameterProvided(ILogger logger);
20+
[LoggerMessage(EventId = 3, Level = LogLevel.Error, Message = "The environment variable AZURE_FEDERATED_TOKEN_FILE or AZURE_ACCESS_TOKEN_FILE or the 'SignedAssertionFileDiskPath' must be set to the path of the file containing the signed assertion. ")]
21+
partial void NoSignedAssertionParameterProvided(ILogger logger);
2222
23-
[LoggerMessage(EventId = 4, Level = LogLevel.Error, Message = "The file `{filePath}` containing the signed assertion was not found. ")]
24-
partial void FileAssertionPathNotFound(ILogger logger, string filePath);
23+
[LoggerMessage(EventId = 4, Level = LogLevel.Error, Message = "The file `{filePath}` containing the signed assertion was not found. ")]
24+
partial void FileAssertionPathNotFound(ILogger logger, string filePath);
2525
26-
[LoggerMessage(EventId = 5, Level = LogLevel.Information, Message = "Successfully read the signed assertion for `{filePath}`. Expires at {expiry}. ")]
27-
partial void SuccessFullyReadSignedAssertion(ILogger logger, string filePath, DateTime expiry);
26+
[LoggerMessage(EventId = 5, Level = LogLevel.Information, Message = "Successfully read the signed assertion for `{filePath}`. Expires at {expiry}. ")]
27+
partial void SuccessFullyReadSignedAssertion(ILogger logger, string filePath, DateTime expiry);
2828
29-
[LoggerMessage(EventId = 6, Level = LogLevel.Error, Message = "The file `{filePath} does not contain a valid signed assertion. {message}. ")]
30-
partial void FileDoesNotContainValidAssertion(ILogger logger, string filePath, string message);
31-
#pragma warning restore SYSLIB1009 // Logging methods must be static
32-
*/
29+
[LoggerMessage(EventId = 6, Level = LogLevel.Error, Message = "The file `{filePath} does not contain a valid signed assertion. {message}. ")]
30+
partial void FileDoesNotContainValidAssertion(ILogger logger, string filePath, string message);
31+
#pragma warning restore SYSLIB1009 // Logging methods must be static
32+
*/
3333

3434
/// <summary>
3535
/// Performant logging messages.
@@ -84,6 +84,7 @@ public static void FileDoesNotContainValidAssertion(ILogger? logger, string file
8484
}
8585
}
8686

87+
8788
private static readonly Action<ILogger, Exception?> __SignedAssertionFileDiskPathNotProvidedCallback =
8889
LoggerMessage.Define(LogLevel.Information, new EventId(1, nameof(SignedAssertionFileDiskPathNotProvided)), "SignedAssertionFileDiskPath not provided. Falling back to the content of the AZURE_FEDERATED_TOKEN_FILE environment variable. ");
8990
private static readonly Action<ILogger, string, Exception?> __SignedAssertionEnvironmentVariableNotProvidedCallback =
@@ -100,6 +101,7 @@ public static void FileDoesNotContainValidAssertion(ILogger? logger, string file
100101

101102
private static readonly Action<ILogger, string, string, Exception?> __FileDoesNotContainValidAssertionCallback =
102103
LoggerMessage.Define<string, string>(LogLevel.Error, new EventId(6, nameof(FileDoesNotContainValidAssertion)), "The file `{filePath} does not contain a valid signed assertion. {message}. ");
104+
103105
}
104106
}
105107
}

0 commit comments

Comments
 (0)