@@ -19,10 +19,15 @@ namespace Azure.Identity
1919 public class TokenCache
2020#pragma warning restore CA1001 // Types that own disposable fields should be disposable
2121 {
22- private SemaphoreSlim _lock = new SemaphoreSlim ( 1 , 1 ) ;
23- private byte [ ] _data ;
22+ private readonly SemaphoreSlim _lock = new SemaphoreSlim ( 1 , 1 ) ;
2423 private DateTimeOffset _lastUpdated ;
2524 private ConditionalWeakTable < object , CacheTimestamp > _cacheAccessMap ;
25+ internal Func < IPublicClientApplication > _publicClientApplicationFactory ;
26+
27+ /// <summary>
28+ /// The internal state of the cache.
29+ /// </summary>
30+ internal byte [ ] Data { get ; private set ; }
2631
2732 private class CacheTimestamp
2833 {
@@ -33,9 +38,10 @@ public CacheTimestamp()
3338 Update ( ) ;
3439 }
3540
36- public void Update ( )
41+ public DateTimeOffset Update ( )
3742 {
3843 _timestamp = DateTimeOffset . UtcNow ;
44+ return _timestamp ;
3945 }
4046
4147 public DateTimeOffset Value { get { return _timestamp ; } }
@@ -49,98 +55,19 @@ public TokenCache()
4955 {
5056 }
5157
52- internal TokenCache ( byte [ ] data )
58+ internal TokenCache ( byte [ ] data , Func < IPublicClientApplication > publicApplicationFactory = null )
5359 {
54- _data = data ;
60+ Data = data ;
5561 _lastUpdated = DateTimeOffset . UtcNow ;
5662 _cacheAccessMap = new ConditionalWeakTable < object , CacheTimestamp > ( ) ;
63+ _publicClientApplicationFactory = publicApplicationFactory ?? new ( ( ) => PublicClientApplicationBuilder . Create ( Guid . NewGuid ( ) . ToString ( ) ) . Build ( ) ) ;
5764 }
5865
5966 /// <summary>
6067 /// An event notifying the subscriber that the underlying <see cref="TokenCache"/> has been updated. This event can be handled to persist the updated cache data.
6168 /// </summary>
6269 public event Func < TokenCacheUpdatedArgs , Task > Updated ;
6370
64- /// <summary>
65- /// Serializes the <see cref="TokenCache"/> to the specified <see cref="Stream"/>.
66- /// </summary>
67- /// <param name="stream">The <see cref="Stream"/> which the serialized <see cref="TokenCache"/> will be written to.</param>
68- /// <param name="cancellationToken">A <see cref="CancellationToken"/> controlling the request lifetime.</param>
69- public void Serialize ( Stream stream , CancellationToken cancellationToken = default )
70- {
71- if ( stream is null )
72- throw new ArgumentNullException ( nameof ( stream ) ) ;
73-
74- SerializeAsync ( stream , false , cancellationToken ) . EnsureCompleted ( ) ;
75- }
76-
77- /// <summary>
78- /// Serializes the <see cref="TokenCache"/> to the specified <see cref="Stream"/>.
79- /// </summary>
80- /// <param name="stream">The <see cref="Stream"/> to which the serialized <see cref="TokenCache"/> will be written.</param>
81- /// <param name="cancellationToken">A <see cref="CancellationToken"/> controlling the request lifetime.</param>
82- public async Task SerializeAsync ( Stream stream , CancellationToken cancellationToken = default )
83- {
84- if ( stream is null )
85- throw new ArgumentNullException ( nameof ( stream ) ) ;
86-
87- await SerializeAsync ( stream , true , cancellationToken ) . ConfigureAwait ( false ) ;
88- }
89-
90- /// <summary>
91- /// Deserializes the <see cref="TokenCache"/> from the specified <see cref="Stream"/>.
92- /// </summary>
93- /// <param name="stream">The <see cref="Stream"/> from which the serialized <see cref="TokenCache"/> will be read.</param>
94- /// <param name="cancellationToken">A <see cref="CancellationToken"/> controlling the request lifetime.</param>
95- public static TokenCache Deserialize ( Stream stream , CancellationToken cancellationToken = default )
96- {
97- if ( stream is null )
98- throw new ArgumentNullException ( nameof ( stream ) ) ;
99-
100- return DeserializeAsync ( stream , false , cancellationToken ) . EnsureCompleted ( ) ;
101- }
102-
103- /// <summary>
104- /// Deserializes the <see cref="TokenCache"/> from the specified <see cref="Stream"/>.
105- /// </summary>
106- /// <param name="stream">The <see cref="Stream"/> from which the serialized <see cref="TokenCache"/> will be read.</param>
107- /// <param name="cancellationToken">A <see cref="CancellationToken"/> controlling the request lifetime.</param>
108- public static async Task < TokenCache > DeserializeAsync ( Stream stream , CancellationToken cancellationToken = default )
109- {
110- if ( stream is null )
111- throw new ArgumentNullException ( nameof ( stream ) ) ;
112-
113- return await DeserializeAsync ( stream , true , cancellationToken ) . ConfigureAwait ( false ) ;
114- }
115-
116- private async Task SerializeAsync ( Stream stream , bool async , CancellationToken cancellationToken )
117- {
118- if ( async)
119- {
120- await stream . WriteAsync ( _data , 0 , _data . Length , cancellationToken ) . ConfigureAwait ( false ) ;
121- }
122- else
123- {
124- stream . Write ( _data , 0 , _data . Length ) ;
125- }
126- }
127-
128- private static async Task < TokenCache > DeserializeAsync ( Stream stream , bool async , CancellationToken cancellationToken )
129- {
130- var data = new byte [ stream . Length - stream . Position ] ;
131-
132- if ( async)
133- {
134- await stream . ReadAsync ( data , 0 , data . Length , cancellationToken ) . ConfigureAwait ( false ) ;
135- }
136- else
137- {
138- stream . Read ( data , 0 , data . Length ) ;
139- }
140-
141- return new TokenCache ( data ) ;
142- }
143-
14471 internal virtual async Task RegisterCache ( bool async , ITokenCache tokenCache , CancellationToken cancellationToken )
14572 {
14673 if ( async)
@@ -175,7 +102,7 @@ private async Task OnBeforeCacheAccessAsync(TokenCacheNotificationArgs args)
175102
176103 try
177104 {
178- args. TokenCache . DeserializeMsalV3 ( _data , true ) ;
105+ args. TokenCache . DeserializeMsalV3 ( Data , true ) ;
179106
180107 _cacheAccessMap. GetOrCreateValue ( args . TokenCache ) . Update ( ) ;
181108 }
@@ -201,16 +128,14 @@ private async Task UpdateCacheDataAsync(ITokenCacheSerializer tokenCache)
201128 {
202129 if ( ! _cacheAccessMap . TryGetValue ( tokenCache , out CacheTimestamp lastRead ) || lastRead . Value < _lastUpdated )
203130 {
204- _data = await MergeCacheData( _data , tokenCache . SerializeMsalV3 ( ) ) . ConfigureAwait ( false ) ;
131+ Data = await MergeCacheData( Data , tokenCache . SerializeMsalV3 ( ) ) . ConfigureAwait ( false ) ;
205132 }
206133 else
207134 {
208- _data = tokenCache. SerializeMsalV3 ( ) ;
135+ Data = tokenCache. SerializeMsalV3 ( ) ;
209136 }
210137
211- _cacheAccessMap. GetOrCreateValue ( tokenCache ) . Update ( ) ;
212-
213- _lastUpdated = DateTime. UtcNow ;
138+ _lastUpdated = _cacheAccessMap. GetOrCreateValue ( tokenCache ) . Update ( ) ;
214139 }
215140 finally
216141 {
@@ -226,11 +151,11 @@ private async Task UpdateCacheDataAsync(ITokenCacheSerializer tokenCache)
226151 }
227152 }
228153
229- private static async Task< byte [ ] > MergeCacheData ( byte [ ] cacheA , byte [ ] cacheB )
154+ private async Task< byte [ ] > MergeCacheData ( byte [ ] cacheA , byte [ ] cacheB )
230155 {
231156 byte [ ] merged = null ;
232157
233- var client = PublicClientApplicationBuilder . Create ( Guid . NewGuid ( ) . ToString ( ) ) . Build ( ) ;
158+ IPublicClientApplication client = _publicClientApplicationFactory ( ) ;
234159
235160 client. UserTokenCache . SetBeforeAccess ( args => args . TokenCache . DeserializeMsalV3 ( cacheA ) ) ;
236161
0 commit comments