Skip to content

Token cache serialization

Jean-Marc Prieur edited this page Mar 8, 2021 · 53 revisions

Token Cache serialization

This article is for MSAL.NET 3.x. If you are still interested in MSAL.NET 2.x, see Token cache serialization in MSAL.NET 2.x

An in memory token cache is provided by default

In MSAL.NET, an in-memory token cache is provided by default. To take advantage of the in memory cache you will have to keep the Client Application in memory as MSAL.NET doesn't have any statics for the cache. Please note this is different from ADAL!

Serialization is provided out of the box on mobile platforms

Additionally, the serialization is provided by default for a certain number of platforms where a secure storage is available for a user as part of the platform. This is the case of UWP, Xamarin.iOS, Xamarin.Android.

Note that when you migrate a Xamarin.Android project from MSAL.NET 1.x to MSAL.NET 3.x, you might want to add | android:allowBackup="false" to your project to avoid old cached tokens from coming back because Visual Studio deployments are triggering a restore of local storage. See #659

Serialization is customizable in Windows desktop apps and Web apps / Web Apis

In the case of .NET Framework and .NET core, if you don't do anything extra, the in-memory token cache lasts for the duration of the application. To understand why serialization is not provided out of the box, remember MSAL .NET desktop/core applications can be console or Windows applications (which would have access to the file system), but also Web applications or Web API, which might use some specific cache mechanisms like databases, distributed caches, Redis caches, etc. To have a persistent token cache application in .NET Desktop or Core, you will need to customize the serialization.

Custom token cache serialization in MSAL.NET

Remember:

This feature is not available on mobile platforms (UWP, Xamarin.iOS, Xamarin.Android) because MSAL already defines a secure and performant serialization mechanism. .NET desktop and .NET Core applications, on the other hand, have varied architectures, and MSAL cannot implement a serialization mechanism that fits all purposes (e.g. web sites may choose to store tokens in a Redis cache, desktop apps in an encrypted file etc.)

The classes and interfaces involved in token cache serialization are the following:

  • ITokenCache, which defines events to subscribe to token cache serialization requests, as well as methods to serialize or de-serialize the cache at various formats (ADAL v3.0, MSAL 2.x and MSAL 3.x = ADAL v5.0)

  • TokenCacheCallback is a callback passed to the events so that you can handle the serialization. they will be called with arguments of type TokenCacheNotificationArgs.

  • TokenCacheNotificationArgs only provides the ClientId of the application and a reference to the user for which the token is available

    image

Important MSAL.NET creates token caches for you and provides you with the IToken cache. Then, you read the caches using the application's UserTokenCache and AppTokenCache properties. You are not supposed to implement the interface yourself. Your responsibility, when you implement a custom token cache serialization, is to:

  • React to BeforeAccess and AfterAccess "events". TheBeforeAccess delegate is responsible to deserialize the cache, whereas the AfterAccess one is responsible for serializing the cache.
  • Part of these events store or load blobs, which are passed through the event argument to whatever storage you want.

The strategies are different depending on if you are writing a token cache serialization for a public client application (Desktop), or a confidential client application (Web App / Web API, Daemon app).

Token cache for a public client application

Since MSAL V2.x you have several options, depending on if you want to serialize the cache only to the MSAL.NET format (unified format cache which is common with MSAL, but also across the platforms), or if you also want to also support the legacy Token cache serialization of ADAL V3.

The customization of Token cache serialization to share the SSO state between ADAL.NET 3.x, ADAL.NET 5.x and MSAL.NET is explained in part of the following sample: active-directory-dotnet-v1-to-v2

Note: The MSAL.NET 1.1.4-preview token cache format is no longer supported in MSAL 2.x. If you have applications leveraging MSAL.NET 1.x, your users will have to re-sign-in. On the other hand, the migration from ADAL 4.x (and 3.x) is supported.

Cross platform token cache (MSAL only)

MSAL.NET provides a cross platform token cache in a separate library named Microsoft.Identity.Client.Extensions.Msal, which source code is available from https://github.com/AzureAD/microsoft-authentication-extensions-for-dotnet.

Referencing the NuGet package

Add the Microsoft.Identity.Client.Extensions.Msal NuGet package to your project.

Configuring the token cache

See See https://github.com/AzureAD/microsoft-authentication-extensions-for-dotnet/wiki/Cross-platform-Token-Cache for details. Here is an example of usage of the cross platform token cache.

 var storageProperties =
     new StorageCreationPropertiesBuilder(Config.CacheFileName, Config.CacheDir, Config.ClientId)
     .WithLinuxKeyring(
         Config.LinuxKeyRingSchema,
         Config.LinuxKeyRingCollection,
         Config.LinuxKeyRingLabel,
         Config.LinuxKeyRingAttr1,
         Config.LinuxKeyRingAttr2)
     .WithMacKeyChain(
         Config.KeyChainServiceName,
         Config.KeyChainAccountName)
     .Build();

 IPublicClientApplication pca = PublicClientApplicationBuilder.Create(clientId)
    .WithAuthority(Config.Authority)
    .WithRedirectUri("http://localhost")  // make sure to register this redirect URI for the interactive login 
    .Build();
    

// This hooks up the cross-platform cache into MSAL
var cacheHelper = await MsalCacheHelper.CreateAsync(storageProperties );
cacheHelper.RegisterCache(pca.UserTokenCache);
         

Examples of token cache custom serialization for public client applications

See Custom token cache in public client applications for implementations of custom serialization of a token cache for desktop applications

Token cache for a web app (confidential client application)

In the case of web apps or web APIs, caching should be handled differently than for public client applications. It's highly recommended to leverage a token cache serializer, which can be distributed cache, (e.g. Redis, Cosmos, or SQL Server, distributed in memory cache), or a correctly partitioned in memory cache.

Important:

For web apps and web APIs, there should be one token cache per user (per account) and thus the cache should be serialized per account. The TokenCacheNotificationArgs contains a cache key which can be used to partition the cache.

Indeed, by default MSAL's cache has all the accounts with their tokens in memory. By using token cache serializers you partition the token caches depending on the cache key that is used because the cache is swapped between the storage (which can be memory) and MSAL memory each time it's needed. This means MSAL.NET only has very small caches at once in its memory. This is safer (you can only access what you should access to), and more performant. The partition is key-ed by the TokenCacheNotificationArgs.SuggestedCacheKey

image

If you are using ASP.NET Core or ASP.NET on .NET Framework, Microsoft.Identity.Web provides token cache serialization for you:

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 TokenCacheProviders folder in the Microsoft.Identity.Web repository.

Microsoft.Identity.Web is available 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:

IConfidentialClientApplication cca = 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(...)

Disabling legacy token cache

MSAL has some internal code specifically to enable the ability to interact with legacy ADAL cache. When MSAL and ADAL are not used side by side (therefore the legacy cache is not used), the related legacy cache code is unnecessary. MSAL 4.25.0 adds the ability to disable legacy ADAL cache code and improve cache usage performance. See pull request #2309 for performance comparison before and after disabling the legacy cache. Call .WithLegacyCacheCompatibility(false) on an application builder like below.

var app = ConfidentialClientApplicationBuilder
	.Create(clientId)
	.WithClientSecret(clientSecret)
	.WithLegacyCacheCompatibility(false)
	.Build();

Some of the samples illustrating token cache serialization

Sample Platform Description
active-directory-dotnet-desktop-msgraph-v2 Desktop (WPF) Windows Desktop .NET (WPF) application calling the Microsoft Graph API.
active-directory-dotnet-v1-to-v2 Desktop (Console) Set of Visual Studio solutions illustrating the migration of Azure AD v1.0 applications (using ADAL.NET) to Azure AD v2.0 applications, also named converged applications (using MSAL.NET), in particular Token Cache Migration
ms-identity-aspnet-webapp-openidconnect ASP.NET (net472) Example of token cache serialization in an ASP.NET MVC application (using MSAL.NET). See in particular MsalAppBuilder

Getting started with MSAL.NET

Acquiring tokens

Desktop/Mobile apps

Web Apps / Web APIs / daemon apps

Advanced topics

News

FAQ

Other resources

Clone this wiki locally