Skip to content
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
3190435
Add cache details to token cache notification args
neha-bhargava Oct 6, 2022
2b2d6fd
Change code to make it more readable.
neha-bhargava Oct 7, 2022
4fe6bd7
Address comments
neha-bhargava Oct 7, 2022
169d95c
Address comments
neha-bhargava Oct 7, 2022
969867c
Remove unused imports
neha-bhargava Oct 11, 2022
c233b72
Change from dictionary to class
neha-bhargava Oct 12, 2022
09d2d35
Update comments and CacheUsed enum values
neha-bhargava Oct 12, 2022
c537f87
Merge branch 'main' into nebharg/AddCacheDetailsTelemetry
neha-bhargava Oct 12, 2022
fd6dd23
Removing the enum as this can be inferred from Latencies
neha-bhargava Oct 15, 2022
5449a7d
Merge branch 'nebharg/AddCacheDetailsTelemetry' of https://github.com…
neha-bhargava Oct 15, 2022
bd32bba
Merge remote-tracking branch 'origin/pmaytak/1' into nebharg/AddCache…
Mar 7, 2023
39ed3f0
Adding api event for credential type.
Mar 9, 2023
02dd568
Updating telemetry Data
Mar 10, 2023
e9e9ceb
Adding tests for new telemetry datapoints.
Mar 15, 2023
1b9644f
Adding telemetry cache test.
Mar 15, 2023
057e407
Merge branch 'main' into nebharg/AddCacheDetailsTelemetry
trwalke Mar 15, 2023
8477fc4
Merge remote-tracking branch 'origin/pmaytak/1' into nebharg/AddCache…
Mar 29, 2023
2df0d13
Refactoring
Mar 29, 2023
43ad450
Merge branch 'main' into nebharg/AddCacheDetailsTelemetry
Apr 20, 2023
18f153b
Refactoring
Apr 20, 2023
df4da2f
Adding scopes, resources and error message to telemetry
Apr 21, 2023
d7c655f
Apply suggestions from code review
trwalke Apr 25, 2023
2d1eb42
Merge remote-tracking branch 'origin/main' into nebharg/AddCacheDetai…
Apr 25, 2023
35b5c73
Updating scope parsing logic.
Apr 27, 2023
8a66797
Refactoring
Apr 28, 2023
1a88f21
Updating telemetry data points
May 3, 2023
1823be2
Merge branch 'main' into nebharg/AddCacheDetailsTelemetry
trwalke May 3, 2023
40d62f8
Test update
May 3, 2023
15896d2
Fix typo
May 3, 2023
d64f181
Test Fixes
May 3, 2023
9346d15
Merge branch 'main' into nebharg/AddCacheDetailsTelemetry
trwalke May 3, 2023
24d2bc3
Apply suggestions from code review
trwalke May 16, 2023
4e1bf52
Adding additional test
May 17, 2023
657a8cc
Merge branch 'main' into nebharg/AddCacheDetailsTelemetry
May 23, 2023
40a22d0
test fix
May 23, 2023
0be7992
Test Fix
May 23, 2023
cb4667f
Test Update
May 23, 2023
059b6e4
Merge branch 'main' into nebharg/AddCacheDetailsTelemetry
gladjohn May 23, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,6 @@ public X509Certificate2 ClientCredentialCertificate
return null;
}
}

#endregion

#region Region
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -341,8 +341,6 @@ public ConfidentialClientApplicationBuilder WithGenericAuthority(string authorit
/// <returns>The builder to chain the .With methods</returns>
public ConfidentialClientApplicationBuilder WithTelemetryClient(params ITelemetryClient[] telemetryClients)
{
ValidateUseOfExperimentalFeature("ITelemetryClient");

if (telemetryClients == null)
{
throw new ArgumentNullException(nameof(telemetryClients));
Expand Down
35 changes: 35 additions & 0 deletions src/client/Microsoft.Identity.Client/Cache/CacheLevel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Microsoft.Identity.Client.Cache
{
/// <summary>
/// Identifies the type of cache that the token was read from. Cache implementations must provide this.
/// </summary>
public enum CacheLevel
{
/// <summary>
/// Specifies that the cache level used is None.
/// Token was retrieved from ESTS
/// </summary>
None = 0,
/// <summary>
/// Specifies that the cache level used is unknown.
/// Token was retrieved from cache but the token cache implementation didn't specify which cache level was used.
/// </summary>
Unknown = 1,
/// <summary>
/// Specifies if the L1 cache is used.
/// </summary>
L1Cache = 2,
/// <summary>
/// Specifies if the L2 cache is used.
L2Cache = 3
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Microsoft.Identity.Client.Internal;
using Microsoft.Identity.Client.Internal.Requests;
using Microsoft.Identity.Client.OAuth2;
using Microsoft.Identity.Client.TelemetryCore.TelemetryClient;

namespace Microsoft.Identity.Client.Cache
{
Expand Down Expand Up @@ -106,6 +107,7 @@ private async Task RefreshCacheForReadOperationsAsync()
await TokenCacheInternal.Semaphore.WaitAsync(_requestParams.RequestContext.UserCancellationToken).ConfigureAwait(false);
_requestParams.RequestContext.Logger.Verbose(()=>"[Cache Session Manager] Entered cache semaphore");

TelemetryData telemetryData = new TelemetryData();
Stopwatch stopwatch = new Stopwatch();
try
{
Expand All @@ -129,7 +131,8 @@ private async Task RefreshCacheForReadOperationsAsync()
requestScopes: _requestParams.Scope,
requestTenantId: _requestParams.AuthorityManager.OriginalAuthority.TenantId,
identityLogger: _requestParams.RequestContext.Logger.IdentityLogger,
piiLoggingEnabled: _requestParams.RequestContext.Logger.PiiLoggingEnabled);
piiLoggingEnabled: _requestParams.RequestContext.Logger.PiiLoggingEnabled,
telemetryData: telemetryData);

stopwatch.Start();
await TokenCacheInternal.OnBeforeAccessAsync(args).ConfigureAwait(false);
Expand All @@ -155,7 +158,8 @@ private async Task RefreshCacheForReadOperationsAsync()
requestScopes: _requestParams.Scope,
requestTenantId: _requestParams.AuthorityManager.OriginalAuthority.TenantId,
identityLogger: _requestParams.RequestContext.Logger.IdentityLogger,
piiLoggingEnabled: _requestParams.RequestContext.Logger.PiiLoggingEnabled);
piiLoggingEnabled: _requestParams.RequestContext.Logger.PiiLoggingEnabled,
telemetryData: telemetryData);

await TokenCacheInternal.OnAfterAccessAsync(args).ConfigureAwait(false);
RequestContext.ApiEvent.DurationInCacheInMs += stopwatch.ElapsedMilliseconds;
Expand All @@ -170,6 +174,7 @@ private async Task RefreshCacheForReadOperationsAsync()
{
TokenCacheInternal.Semaphore.Release();
_requestParams.RequestContext.Logger.Verbose(()=>"[Cache Session Manager] Released cache semaphore");
RequestContext.ApiEvent.CacheLevel = telemetryData.CacheLevel;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Microsoft.Identity.Client.Core;
using Microsoft.Identity.Client.OAuth2;
using Microsoft.Identity.Client.PlatformsCommon.Interfaces;
using Microsoft.Identity.Client.TelemetryCore;
using Microsoft.Identity.Client.Utils;

namespace Microsoft.Identity.Client.Internal.ClientCredential
Expand All @@ -19,6 +20,8 @@ internal class CertificateAndClaimsClientCredential : IClientCredential
private readonly string _base64EncodedThumbprint; // x5t
public X509Certificate2 Certificate { get; }

public AssertionType AssertionType => AssertionType.CertificateWithoutSni;

public CertificateAndClaimsClientCredential(X509Certificate2 certificate, IDictionary<string, string> claimsToSign, bool appendDefaultClaims)
{
Certificate = certificate;
Expand All @@ -42,7 +45,7 @@ public Task AddConfidentialClientParametersAsync(
tokenEndpoint,
_claimsToSign,
_appendDefaultClaims);

string assertion = jwtToken.Sign(Certificate, _base64EncodedThumbprint, sendX5C);

oAuth2Client.AddBodyParameter(OAuth2Parameter.ClientAssertionType, OAuth2AssertionType.JwtBearer);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@
using Microsoft.Identity.Client.Core;
using Microsoft.Identity.Client.OAuth2;
using Microsoft.Identity.Client.PlatformsCommon.Interfaces;
using Microsoft.Identity.Client.TelemetryCore;
using Microsoft.Identity.Client.Utils;

namespace Microsoft.Identity.Client.Internal.ClientCredential
{
internal interface IClientCredential
{
AssertionType AssertionType { get; }

Task AddConfidentialClientParametersAsync(
OAuth2Client oAuth2Client,
ILoggerAdapter logger,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Microsoft.Identity.Client.Core;
using Microsoft.Identity.Client.OAuth2;
using Microsoft.Identity.Client.PlatformsCommon.Interfaces;
using Microsoft.Identity.Client.TelemetryCore;
using Microsoft.Identity.Client.Utils;

namespace Microsoft.Identity.Client.Internal.ClientCredential
Expand All @@ -14,6 +15,8 @@ internal class SecretStringClientCredential : IClientCredential
{
internal string Secret { get; }

public AssertionType AssertionType => AssertionType.Secret;

public SecretStringClientCredential(string secret)
{
Secret = secret;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Microsoft.Identity.Client.Core;
using Microsoft.Identity.Client.OAuth2;
using Microsoft.Identity.Client.PlatformsCommon.Interfaces;
using Microsoft.Identity.Client.TelemetryCore;
using Microsoft.Identity.Client.Utils;

namespace Microsoft.Identity.Client.Internal.ClientCredential
Expand All @@ -14,10 +15,13 @@ internal class SignedAssertionClientCredential : IClientCredential
{
private readonly string _signedAssertion;

public AssertionType AssertionType => AssertionType.ClientAssertion;

public SignedAssertionClientCredential(string signedAssertion)
{
_signedAssertion = signedAssertion;
}

public Task AddConfidentialClientParametersAsync(OAuth2Client oAuth2Client, ILoggerAdapter logger, ICryptographyManager cryptographyManager, string clientId, string tokenEndpoint, bool sendX5C, CancellationToken cancellationToken)
{
oAuth2Client.AddBodyParameter(OAuth2Parameter.ClientAssertionType, OAuth2AssertionType.JwtBearer);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
using Microsoft.Identity.Client.Core;
using Microsoft.Identity.Client.OAuth2;
using Microsoft.Identity.Client.PlatformsCommon.Interfaces;
using Microsoft.Identity.Client.TelemetryCore;

namespace Microsoft.Identity.Client.Internal.ClientCredential
{
internal class SignedAssertionDelegateClientCredential : IClientCredential
{
internal Func<CancellationToken, Task<string>> _signedAssertionDelegate { get; }
internal Func<AssertionRequestOptions, Task<string>> _signedAssertionWithInfoDelegate { get; }
public AssertionType AssertionType => AssertionType.ClientAssertion;

[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
public SignedAssertionDelegateClientCredential(Func<CancellationToken, Task<string>> signedAssertionDelegate)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Threading;
using Microsoft.Identity.Client.Core;
using Microsoft.Identity.Client.Internal.Logger;
using Microsoft.Identity.Client.TelemetryCore;
using Microsoft.Identity.Client.TelemetryCore.Internal.Events;
using Microsoft.Identity.Client.TelemetryCore.TelemetryClient;
using Microsoft.IdentityModel.Abstractions;

namespace Microsoft.Identity.Client.Internal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,14 @@ public async Task<AuthenticationResult> RunAsync(CancellationToken cancellationT
{
apiEvent.ApiErrorCode = ex.ErrorCode;
AuthenticationRequestParameters.RequestContext.Logger.ErrorPii(ex);
LogErrorTelemetryToClient(ex.ErrorCode, telemetryEventDetails, telemetryClients);
LogMsalErrorTelemetryToClient(ex, telemetryEventDetails, telemetryClients);
throw;
}
catch (Exception ex)
{
apiEvent.ApiErrorCode = ex.GetType().Name;
AuthenticationRequestParameters.RequestContext.Logger.ErrorPii(ex);
LogMsalErrorTelemetryToClient(ex, telemetryEventDetails, telemetryClients);
throw;
}
finally
Expand All @@ -120,12 +121,27 @@ public async Task<AuthenticationResult> RunAsync(CancellationToken cancellationT
}
}

private void LogErrorTelemetryToClient(string errorCode, MsalTelemetryEventDetails telemetryEventDetails, ITelemetryClient[] telemetryClients)
private void LogMsalErrorTelemetryToClient(Exception ex, MsalTelemetryEventDetails telemetryEventDetails, ITelemetryClient[] telemetryClients)
{
if (telemetryClients.HasEnabledClients(TelemetryConstants.AcquireTokenEventName))
{
telemetryEventDetails.SetProperty(TelemetryConstants.Succeeded, false);
telemetryEventDetails.SetProperty(TelemetryConstants.ErrorCode, errorCode);
telemetryEventDetails.SetProperty(TelemetryConstants.ErrorMessage, ex.Message);

if (ex is MsalClientException clientException)
{
telemetryEventDetails.SetProperty(TelemetryConstants.ErrorCode, clientException.ErrorCode);
return;
}

if (ex is MsalServiceException serviceException)
{
telemetryEventDetails.SetProperty(TelemetryConstants.ErrorCode, serviceException.ErrorCode);
telemetryEventDetails.SetProperty(TelemetryConstants.StsErrorCode, serviceException.ErrorCodes?.FirstOrDefault());
return;
}

telemetryEventDetails.SetProperty(TelemetryConstants.ErrorCode, ex.GetType().ToString());
}
}

Expand All @@ -139,12 +155,66 @@ private void LogSuccessfulTelemetryToClient(AuthenticationResult authenticationR
telemetryEventDetails.SetProperty(TelemetryConstants.DurationInCache, authenticationResult.AuthenticationResultMetadata.DurationInCacheInMs);
telemetryEventDetails.SetProperty(TelemetryConstants.DurationInHttp, authenticationResult.AuthenticationResultMetadata.DurationInHttpInMs);
telemetryEventDetails.SetProperty(TelemetryConstants.Succeeded, true);
telemetryEventDetails.SetProperty(TelemetryConstants.PopToken, authenticationResult.TokenType.Equals(Constants.PoPTokenType));
telemetryEventDetails.SetProperty(TelemetryConstants.TokenType, (int)AuthenticationRequestParameters.RequestContext.ApiEvent.TokenType);
telemetryEventDetails.SetProperty(TelemetryConstants.RemainingLifetime, (authenticationResult.ExpiresOn - DateTime.Now).TotalMilliseconds);
telemetryEventDetails.SetProperty(TelemetryConstants.ActivityId, authenticationResult.CorrelationId);

if (authenticationResult.AuthenticationResultMetadata.RefreshOn.HasValue)
{
telemetryEventDetails.SetProperty(TelemetryConstants.RefreshOn, DateTimeHelpers.DateTimeToUnixTimestampMilliseconds(authenticationResult.AuthenticationResultMetadata.RefreshOn.Value));
}
telemetryEventDetails.SetProperty(TelemetryConstants.AssertionType, (int)AuthenticationRequestParameters.RequestContext.ApiEvent.AssertionType);
telemetryEventDetails.SetProperty(TelemetryConstants.Endpoint, AuthenticationRequestParameters.Authority.AuthorityInfo.CanonicalAuthority.ToString());
telemetryEventDetails.SetProperty(TelemetryConstants.CacheLevel, (int)GetCacheLevel(authenticationResult));
ParseScopesForTelemetry(telemetryEventDetails);
}
}

private void ParseScopesForTelemetry(MsalTelemetryEventDetails telemetryEventDetails)
{
if (AuthenticationRequestParameters.Scope.Count > 0)
{
string firstScope = AuthenticationRequestParameters.Scope.First();

if (Uri.IsWellFormedUriString(firstScope, UriKind.Absolute))
{
Uri firstScopeAsUri = new Uri(firstScope);
telemetryEventDetails.SetProperty(TelemetryConstants.Resource, $"{firstScopeAsUri.Scheme}://{firstScopeAsUri.Host}");

StringBuilder stringBuilder = new StringBuilder();

foreach (string scope in AuthenticationRequestParameters.Scope)
{
var splitString = scope.Split(new[] { firstScopeAsUri.Host }, StringSplitOptions.None);
string scopeToAppend = splitString.Count() > 1 ? splitString[1].TrimStart('/') + " " : splitString.FirstOrDefault();
stringBuilder.Append(scopeToAppend);
}

telemetryEventDetails.SetProperty(TelemetryConstants.Scopes, stringBuilder.ToString().TrimEnd(' '));
}
else
{
telemetryEventDetails.SetProperty(TelemetryConstants.Scopes, AuthenticationRequestParameters.Scope.AsSingleString());
}
}
}

private CacheLevel GetCacheLevel(AuthenticationResult authenticationResult)
{
if (authenticationResult.AuthenticationResultMetadata.TokenSource == TokenSource.Cache) //Check if token source is cache
{
if (AuthenticationRequestParameters.RequestContext.ApiEvent.CacheLevel > CacheLevel.Unknown) //Check if cache has indicated which level was used
{
return AuthenticationRequestParameters.RequestContext.ApiEvent.CacheLevel;
}

//If no level was used, set to unknown
return CacheLevel.Unknown;
}

return CacheLevel.None;
}

private static void LogMetricsFromAuthResult(AuthenticationResult authenticationResult, ILoggerAdapter logger)
{
if (logger.IsLoggingEnabled(LogLevel.Always))
Expand Down Expand Up @@ -193,13 +263,40 @@ private ApiEvent InitializeApiEvent(string accountId)
apiEvent.IsLegacyCacheEnabled = AuthenticationRequestParameters.RequestContext.ServiceBundle.Config.LegacyCacheCompatibilityEnabled;
apiEvent.CacheInfo = CacheRefreshReason.NotApplicable;
apiEvent.TokenType = AuthenticationRequestParameters.AuthenticationScheme.TelemetryTokenType;
apiEvent.AssertionType = GetAssertionType();

// Give derived classes the ability to add or modify fields in the telemetry as needed.
EnrichTelemetryApiEvent(apiEvent);

return apiEvent;
}

private AssertionType GetAssertionType()
{
if (ServiceBundle.Config.IsManagedIdentity ||
ServiceBundle.Config.AppTokenProvider != null)
{
return AssertionType.ManagedIdentity;
}

if (ServiceBundle.Config.ClientCredential != null)
{
if (ServiceBundle.Config.ClientCredential.AssertionType == AssertionType.CertificateWithoutSni)
{
if (ServiceBundle.Config.SendX5C)
{
return AssertionType.CertificateWithSni;
}

return AssertionType.CertificateWithoutSni;
}

return ServiceBundle.Config.ClientCredential.AssertionType;
}

return AssertionType.None;
}

protected async Task<AuthenticationResult> CacheTokenResponseAndCreateAuthenticationResultAsync(MsalTokenResponse msalTokenResponse)
{
// developer passed in user object.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,11 @@ public HttpResponseHeaders Headers
/// </remarks>
internal string SubError { get; set; }

/// <summary>
/// A list of STS-specific error codes that can help in diagnostics.
/// </summary>
internal string[] ErrorCodes { get; set; }

/// <summary>
/// As per discussion with Evo, AAD
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ internal static MsalServiceException FromHttpResponse(
ex.Claims = oAuth2Response?.Claims;
ex.CorrelationId = oAuth2Response?.CorrelationId;
ex.SubError = oAuth2Response?.SubError;
ex.ErrorCodes = oAuth2Response?.ErrorCodes;

return ex;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

namespace Microsoft.Identity.Client.TelemetryCore
{
internal enum AssertionType
{
None = 0,
CertificateWithoutSni = 1,
CertificateWithSni = 2,
Secret = 3,
ClientAssertion = 4,
ManagedIdentity = 5
}
}
Loading