Home > Blockchain >  How to store Azure AD PublicClientApplication AuthenticationResult tokens on disk
How to store Azure AD PublicClientApplication AuthenticationResult tokens on disk

Time:06-21

Using PublicClientApplicationBuilder I build an IPublicClientApplication that I use to access some services within Azure Devops programatically.

This app is short lived however, it spins up, performs a task (in this case triggering builds) then spins down. At this point the tokens gained from an interactive login are lost, and the next time the app runs, I need to login again.

Is there any way to set a token cache to be on disk or a blob store or something so that this app can run, grab a token from the cache and work without me having to login every time?

I call AcquireTokenSilent a second time at the end there, and it works, after I've got my token with DeviceCode, it is able to gain another token silently. But once this app ends and restarts that cache seems to clear. Is there any way to store it somewhere? A cookie/disk etc? Or should this be cached in azure somehow and mine just isn't working? Or needs configured differently?

Application = PublicClientApplicationBuilder.Create(ClientId)
    .WithAuthority(Authority)
    .WithDefaultRedirectUri()
    .Build();


AuthenticationResult result = null;

var accounts = await Application.GetAccountsAsync();
// Try to acquire an access token from the cache. If device code is required, Exception will be thrown.
if (accounts.Any()) //There is never any accounts in the cache, as this is always run from app startup
{
    result = await Application.AcquireTokenSilent(scopes, accounts.FirstOrDefault())
        .ExecuteAsync();
}
else
{
    //This works fine, but I need to interact and login every time.
    result = await Application.AcquireTokenWithDeviceCode(scopes, deviceCodeResult =>
    {
        Console.WriteLine(deviceCodeResult.Message);
        return Task.FromResult(0);
    }).ExecuteAsync();
}

//Now that I have logged in this works fine
accounts = await Application.GetAccountsAsync();
result = await Application.AcquireTokenSilent(scopes, accounts.FirstOrDefault())
    .ExecuteAsync();

CodePudding user response:

Ok, I figured this out. Ideally I should be using an encrypted keychain etc.. and I am aware that this is unsecure, but I will improve with keychain use in the future.

This solution, however unsecure, caches the information to a local file, and it can be used after an application restart.

var directory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var storageProperties =
    new StorageCreationPropertiesBuilder("authcache", directory)
        .WithUnprotectedFile()
        .Build();

var cacheHelper = await MsalCacheHelper.CreateAsync(
    storageProperties).ConfigureAwait(false);

cacheHelper.VerifyPersistence();

// Initialize the MSAL library by building a public client application
Application = PublicClientApplicationBuilder.Create(ClientId)
    .WithAuthority(Authority)
    .WithLegacyCacheCompatibility()
    .WithDefaultRedirectUri()
    .Build();

// This hooks up the cross-platform cache into MSAL
cacheHelper.RegisterCache(Application.UserTokenCache);
  • Related