Well, i ran into the same problem as @andrei0809, and was able to solve my problem by following the advice of @juunas and @JackJia.
I gave a point to @JackJia for pointing to the right place. I will just add some code here for the sake of completion.
Disclaimer
Before i start i will just mention that in my case i am working in a Public Client Application and not in a Confidential Client Application as the OP. Serialization as recommended in the MS documentation is a bit different for both cases, so i will add both below even when i only used that for Public Client Applications.
Why is Custom token serialization not made out of the box in MSAL.NET?
Going back to the docu we learn that:
.Net desktop and .Net core applications, on the other hand, have
varied architectures, and MSAL cannot implement a serialization
mechanism that fits all purposes
Further, it is also important to note that the feature IS default for mobile platforms (UWP, Xamarin).
Persisting Token Cache in public client applications
First of all, you have to create a helper class that take an ITokenCache object and uses its SetBeforeAccess and SetAfterAccess methods to set delegates that will retrieve and update the token cache before and after access respectively:
static class TokenCacheHelper
{
public static void EnableSerialization(ITokenCache tokenCache)
{
tokenCache.SetBeforeAccess(BeforeAccessNotification);
tokenCache.SetAfterAccess(AfterAccessNotification);
}
/// <summary>
/// Path to the token cache
/// </summary>
public static readonly string CacheFilePath = System.Reflection.Assembly.GetExecutingAssembly().Location + ".msalcache.bin3";
private static readonly object FileLock = new object();
private static void BeforeAccessNotification(TokenCacheNotificationArgs args)
{
lock (FileLock)
{
args.TokenCache.DeserializeMsalV3(File.Exists(CacheFilePath)
? ProtectedData.Unprotect(File.ReadAllBytes(CacheFilePath),
null,
DataProtectionScope.CurrentUser)
: null);
}
}
private static void AfterAccessNotification(TokenCacheNotificationArgs args)
{
// if the access operation resulted in a cache update
if (args.HasStateChanged)
{
lock (FileLock)
{
// reflect changesgs in the persistent store
File.WriteAllBytes(CacheFilePath,
ProtectedData.Protect(args.TokenCache.SerializeMsalV3(),
null,
DataProtectionScope.CurrentUser)
);
}
}
}
}
Then, when you have your helper class all that is left is to use it to
"EnableSerialization" on your UserTokenCache object:
app = PublicClientApplicationBuilder.Create(ClientId)
.Build();
TokenCacheHelper.EnableSerialization(app.UserTokenCache);
Persisting Token Cache in confidential client applications
(Since this is not the approach i needed/used, i will just directly refer to the docu)
In the case of Web Apps or Web APIs, the cache can be very different,
leveraging the session, or a Redis cache, or a database.
A very important thing to remember is that for Web Apps and Web APIs,
there should be one token cache per user (per account). You need to
serialize the token cache for each account.
Examples of how to use token caches for Web apps and Web APIs are
available in the ASP.NET Core Web app tutorial in the phase 2-2 Token
Cache. For implementations have a look at the following folder
TokenCacheProviders in the
microsoft-authentication-extensions-for-dotnet library (in the
Microsoft.Identity.Client.Extensions.Web folder. This library could be
available in the future as a NuGet package.
Token cache for a daemon app
When acquiring a token for a service principal, i.e. on behalf of an
application, you use the Confidential Client grant.
In this case, the token issuer (AAD), only emits Access Tokens.
IDTokens are not created because ID Tokens are related to users.
Refresh Tokens are not created for security reasons. The structure of
the token cache is different, as it only focuses on access tokens,
which anyway have short expiration.
To serialize the content of this cache:
ConfidentialClientApplicationBuilder
.Create(s_clientIdForConfidentialApp)
.WithClientSecret(s_confidentialClientSecret)
.Build();
// Instruct MSAL how to serialise the cache, but use AppTokenCache
instead of the UserTokenCache
cca.AppTokenCache.SetBeforeAccess(notificationArgs => ...);
cca.AppTokenCache.SetAfterAccess(notificationArgs => ...);
var result = await cca.AcquireTokenForClientAsync(...) ```
where, although not tried, i belive you can complete the callbacks with the code for AfterAccessNotification and BeforeAccessNotification above in the Public Client Application section.
Hope this helps someone there facing the same problem i was facing!