10
10
using Microsoft . Identity . Client . Core ;
11
11
using Microsoft . Identity . Client . ManagedIdentity ;
12
12
using Microsoft . Identity . Client . OAuth2 ;
13
+ using Microsoft . Identity . Client . PlatformsCommon . Interfaces ;
13
14
using Microsoft . Identity . Client . Utils ;
14
15
15
16
namespace Microsoft . Identity . Client . Internal . Requests
@@ -18,6 +19,7 @@ internal class ManagedIdentityAuthRequest : RequestBase
18
19
{
19
20
private readonly AcquireTokenForManagedIdentityParameters _managedIdentityParameters ;
20
21
private static readonly SemaphoreSlim s_semaphoreSlim = new SemaphoreSlim ( 1 , 1 ) ;
22
+ private readonly ICryptographyManager _cryptoManager ;
21
23
22
24
public ManagedIdentityAuthRequest (
23
25
IServiceBundle serviceBundle ,
@@ -26,72 +28,105 @@ public ManagedIdentityAuthRequest(
26
28
: base ( serviceBundle , authenticationRequestParameters , managedIdentityParameters )
27
29
{
28
30
_managedIdentityParameters = managedIdentityParameters ;
31
+ _cryptoManager = serviceBundle . PlatformProxy . CryptographyManager ;
29
32
}
30
33
31
34
protected override async Task < AuthenticationResult > ExecuteAsync ( CancellationToken cancellationToken )
32
35
{
33
36
AuthenticationResult authResult = null ;
34
37
ILoggerAdapter logger = AuthenticationRequestParameters . RequestContext . Logger ;
35
38
36
- // Skip checking cache when force refresh or claims is specified
37
- if ( _managedIdentityParameters . ForceRefresh || ! string . IsNullOrEmpty ( AuthenticationRequestParameters . Claims ) )
39
+ // 1. FIRST, handle ForceRefresh
40
+ if ( _managedIdentityParameters . ForceRefresh )
38
41
{
39
- _managedIdentityParameters . Claims = AuthenticationRequestParameters . Claims ;
40
42
AuthenticationRequestParameters . RequestContext . ApiEvent . CacheInfo = CacheRefreshReason . ForceRefreshOrClaims ;
41
-
42
- logger . Info ( "[ManagedIdentityRequest] Skipped looking for a cached access token because ForceRefresh or Claims were set. " +
43
- "This means either a force refresh was requested or claims were present." ) ;
43
+ logger . Info ( "[ManagedIdentityRequest] Skipped using the cache because ForceRefresh was set." ) ;
44
+
45
+ // We still respect claims if present
46
+ _managedIdentityParameters . Claims = AuthenticationRequestParameters . Claims ;
44
47
48
+ // Straight to the MI endpoint
45
49
authResult = await GetAccessTokenAsync ( cancellationToken , logger ) . ConfigureAwait ( false ) ;
46
50
return authResult ;
47
51
}
48
52
53
+ // 2. Otherwise, look for a cached token
49
54
MsalAccessTokenCacheItem cachedAccessTokenItem = await GetCachedAccessTokenAsync ( ) . ConfigureAwait ( false ) ;
50
55
51
- // No access token or cached access token needs to be refreshed
56
+ // If we have claims, we do NOT use the cached token (but we still need it to compute the hash).
57
+ if ( ! string . IsNullOrEmpty ( AuthenticationRequestParameters . Claims ) )
58
+ {
59
+ _managedIdentityParameters . Claims = AuthenticationRequestParameters . Claims ;
60
+ AuthenticationRequestParameters . RequestContext . ApiEvent . CacheInfo = CacheRefreshReason . ForceRefreshOrClaims ;
61
+
62
+ // If there is a cached token, compute its hash for the “bad token” scenario
63
+ if ( cachedAccessTokenItem != null )
64
+ {
65
+ string cachedTokenHash = _cryptoManager . CreateSha256Hash ( cachedAccessTokenItem . Secret ) ;
66
+ _managedIdentityParameters . BadTokenHash = cachedTokenHash ;
67
+
68
+ logger . Info ( "[ManagedIdentityRequest] Claims are present. Computed hash of the cached (bad) token. " +
69
+ "Will now request a fresh token from the MI endpoint." ) ;
70
+ }
71
+ else
72
+ {
73
+ logger . Info ( "[ManagedIdentityRequest] Claims are present, but no cached token was found. " +
74
+ "Requesting a fresh token from the MI endpoint without a bad-token hash." ) ;
75
+ }
76
+
77
+ // In both cases, we skip using the cached token and get a new one
78
+ authResult = await GetAccessTokenAsync ( cancellationToken , logger ) . ConfigureAwait ( false ) ;
79
+ return authResult ;
80
+ }
81
+
82
+ // 3. If we have no ForceRefresh and no claims, we can use the cache
52
83
if ( cachedAccessTokenItem != null )
53
84
{
85
+ // Found a valid token in cache
54
86
authResult = CreateAuthenticationResultFromCache ( cachedAccessTokenItem ) ;
55
-
56
87
logger . Info ( "[ManagedIdentityRequest] Access token retrieved from cache." ) ;
57
88
58
89
try
59
- {
60
- var proactivelyRefresh = SilentRequestHelper . NeedsRefresh ( cachedAccessTokenItem ) ;
61
-
62
- // If needed, refreshes token in the background
90
+ {
91
+ // If token is close to expiry, proactively refresh it in the background
92
+ bool proactivelyRefresh = SilentRequestHelper . NeedsRefresh ( cachedAccessTokenItem ) ;
63
93
if ( proactivelyRefresh )
64
94
{
65
95
logger . Info ( "[ManagedIdentityRequest] Initiating a proactive refresh." ) ;
66
96
67
97
AuthenticationRequestParameters . RequestContext . ApiEvent . CacheInfo = CacheRefreshReason . ProactivelyRefreshed ;
68
98
69
99
SilentRequestHelper . ProcessFetchInBackground (
70
- cachedAccessTokenItem ,
71
- ( ) =>
72
- {
73
- // Use a linked token source, in case the original cancellation token source is disposed before this background task completes.
74
- using var tokenSource = CancellationTokenSource . CreateLinkedTokenSource ( cancellationToken ) ;
75
- return GetAccessTokenAsync ( tokenSource . Token , logger ) ;
76
- } , logger , ServiceBundle , AuthenticationRequestParameters . RequestContext . ApiEvent ,
77
- AuthenticationRequestParameters . RequestContext . ApiEvent . CallerSdkApiId ,
78
- AuthenticationRequestParameters . RequestContext . ApiEvent . CallerSdkVersion ) ;
100
+ cachedAccessTokenItem ,
101
+ ( ) =>
102
+ {
103
+ // Use a linked token source, in case the original cts is disposed
104
+ using var tokenSource = CancellationTokenSource . CreateLinkedTokenSource ( cancellationToken ) ;
105
+ return GetAccessTokenAsync ( tokenSource . Token , logger ) ;
106
+ } ,
107
+ logger ,
108
+ ServiceBundle ,
109
+ AuthenticationRequestParameters . RequestContext . ApiEvent ,
110
+ AuthenticationRequestParameters . RequestContext . ApiEvent . CallerSdkApiId ,
111
+ AuthenticationRequestParameters . RequestContext . ApiEvent . CallerSdkVersion ) ;
79
112
}
80
113
}
81
114
catch ( MsalServiceException e )
82
115
{
116
+ // If background refresh fails, we handle the exception
83
117
return await HandleTokenRefreshErrorAsync ( e , cachedAccessTokenItem ) . ConfigureAwait ( false ) ;
84
118
}
85
119
}
86
120
else
87
121
{
88
- // No AT in the cache
122
+ // No cached token
89
123
if ( AuthenticationRequestParameters . RequestContext . ApiEvent . CacheInfo != CacheRefreshReason . Expired )
90
124
{
91
125
AuthenticationRequestParameters . RequestContext . ApiEvent . CacheInfo = CacheRefreshReason . NoCachedAccessToken ;
92
126
}
93
127
94
- logger . Info ( "[ManagedIdentityRequest] No cached access token. Getting a token from the managed identity endpoint." ) ;
128
+ logger . Info ( "[ManagedIdentityRequest] No cached access token found. " +
129
+ "Getting a token from the managed identity endpoint." ) ;
95
130
authResult = await GetAccessTokenAsync ( cancellationToken , logger ) . ConfigureAwait ( false ) ;
96
131
}
97
132
0 commit comments