1- using System ;
1+ // Copyright (c) Microsoft Corporation.
2+ // Licensed under the MIT License.
3+
4+ using System ;
25using System . Collections . Concurrent ;
36using System . Collections . Generic ;
47using System . Linq ;
@@ -14,7 +17,7 @@ namespace Microsoft.Agents.A365.DevTools.Cli.Services;
1417/// <summary>
1518/// Implements Microsoft Graph token acquisition via PowerShell Microsoft.Graph module.
1619/// </summary>
17- public sealed class MicrosoftGraphTokenProvider : IMicrosoftGraphTokenProvider
20+ public sealed class MicrosoftGraphTokenProvider : IMicrosoftGraphTokenProvider , IDisposable
1821{
1922 private readonly CommandExecutor _executor ;
2023 private readonly ILogger < MicrosoftGraphTokenProvider > _logger ;
@@ -24,7 +27,22 @@ public sealed class MicrosoftGraphTokenProvider : IMicrosoftGraphTokenProvider
2427 private readonly ConcurrentDictionary < string , CachedToken > _tokenCache = new ( ) ;
2528 private readonly ConcurrentDictionary < string , SemaphoreSlim > _locks = new ( ) ;
2629
27- private sealed record CachedToken ( string AccessToken , DateTimeOffset ExpiresOnUtc ) ;
30+ private sealed record CachedToken ( string AccessToken , DateTimeOffset ExpiresOnUtc ) ;
31+
32+ private bool _disposed ;
33+ public void Dispose ( )
34+ {
35+ if ( _disposed ) return ;
36+ _disposed = true ;
37+
38+ foreach ( var kvp in _locks )
39+ {
40+ try { kvp . Value . Dispose ( ) ; }
41+ catch { /* no-op */ }
42+ }
43+
44+ _locks . Clear ( ) ;
45+ }
2846
2947 public MicrosoftGraphTokenProvider (
3048 CommandExecutor executor ,
@@ -317,16 +335,17 @@ private static string MakeCacheKey(string tenantId, IEnumerable<string> scopes,
317335 return $ "{ tenantId } ::{ clientAppId ?? "" } ::{ scopeKey } ";
318336 }
319337
320- private static bool TryGetJwtExpiryUtc ( string jwt , out DateTimeOffset expiresOnUtc )
338+ private bool TryGetJwtExpiryUtc ( string jwt , out DateTimeOffset expiresOnUtc )
321339 {
322340 expiresOnUtc = default ;
323341
324342 if ( string . IsNullOrWhiteSpace ( jwt ) ) return false ;
325- var parts = jwt . Split ( '.' ) ;
326- if ( parts . Length != 3 ) return false ;
327343
328344 try
329345 {
346+ var parts = jwt . Split ( '.' ) ;
347+ if ( parts . Length < 2 ) return false ;
348+
330349 var payloadJson = Encoding . UTF8 . GetString ( Base64UrlDecode ( parts [ 1 ] ) ) ;
331350 using var doc = JsonDocument . Parse ( payloadJson ) ;
332351
@@ -338,8 +357,9 @@ private static bool TryGetJwtExpiryUtc(string jwt, out DateTimeOffset expiresOnU
338357 expiresOnUtc = DateTimeOffset . FromUnixTimeSeconds ( expSeconds ) ;
339358 return true ;
340359 }
341- catch
360+ catch ( Exception ex )
342361 {
362+ _logger . LogDebug ( ex , "Failed to parse JWT expiry (exp) from access token." ) ;
343363 return false ;
344364 }
345365 }
0 commit comments