Recommended ADAL Token Cache for Web API?

I am creating a basic .NET web API that is secured using AAD and that uses ADAL to call a downstream API that uses a stream on behalf of ... similar to this Azure sample:

https://github.com/Azure-Samples/active-directory-dotnet-webapi-onbehalfof

What are the best techniques for a token cache that should be used in such a scenario?

  • Is the default cache valid?

  • If you don't have a cache?

    AuthenticationContext authContext = new AuthenticationContext (authority, null)

  • If you have to create your own then is there a good reference implementation to use?

+3


source to share


1 answer


The correct token cache is very subjective for you and depends on your architecture, performance requirements, etc.

The default cache used by ADAL is an in-memory cache, which means it cannot persist across requests received by your API. Also, the default cache used by ADAL.NET is a static class, which means that two different requests to your API can get the same cache object, which is often unexpected, since these two requests can be for different users. Therefore, using the default ADAL cache is generally not recommended - it really depends on how your web server is running.

Instead, we recommend passing null

as a token cache if you can manage a performance hit or would prefer to use your own token cache.



If you want to implement your own cache, it will save your application from having to send an outgoing AAD HTTP request (via ADAL) for every incoming request. An example of ADAL cache implementation using the .NET entity framework is available here and also copied below:

using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
using System.Linq;
using System.Web;

namespace TodoListWebApp.DAL
{

    public class PerWebUserCache
    {
        [Key]
        public int EntryId { get; set; }
        public string webUserUniqueId { get; set; }
        public byte[] cacheBits { get; set; }
        public DateTime LastWrite { get; set; }
    }

    public class EFADALTokenCache: TokenCache
    {
        private TodoListWebAppContext db = new TodoListWebAppContext();
        string User;
        PerWebUserCache Cache;

        // constructor
        public EFADALTokenCache(string user)
        {
           // associate the cache to the current user of the web app
            User = user;

            this.AfterAccess = AfterAccessNotification;
            this.BeforeAccess = BeforeAccessNotification;
            this.BeforeWrite = BeforeWriteNotification;

            // look up the entry in the DB
            Cache = db.PerUserCacheList.FirstOrDefault(c => c.webUserUniqueId == User);
            // place the entry in memory
            this.Deserialize((Cache == null) ? null : Cache.cacheBits);
        }

        // clean up the DB
        public override void Clear()
        {
            base.Clear();
            foreach (var cacheEntry in db.PerUserCacheList)
                db.PerUserCacheList.Remove(cacheEntry);
            db.SaveChanges();
        }

        // Notification raised before ADAL accesses the cache.
        // This is your chance to update the in-memory copy from the DB, if the in-memory version is stale
        void BeforeAccessNotification(TokenCacheNotificationArgs args)
        {
            if (Cache == null)
            {
                // first time access
                Cache = db.PerUserCacheList.FirstOrDefault(c => c.webUserUniqueId == User);
            }
            else
            {   // retrieve last write from the DB
                var status = from e in db.PerUserCacheList
                             where (e.webUserUniqueId == User)
                             select new
                             {
                                 LastWrite = e.LastWrite
                             };
                // if the in-memory copy is older than the persistent copy
                if (status.First().LastWrite > Cache.LastWrite)
                //// read from from storage, update in-memory copy
                {
                    Cache = db.PerUserCacheList.FirstOrDefault(c => c.webUserUniqueId == User);
                }
            }
            this.Deserialize((Cache == null) ? null : Cache.cacheBits);
        }
        // Notification raised after ADAL accessed the cache.
        // If the HasStateChanged flag is set, ADAL changed the content of the cache
        void AfterAccessNotification(TokenCacheNotificationArgs args)
        {
            // if state changed
            if (this.HasStateChanged)
            {
                Cache = new PerWebUserCache
                {
                    webUserUniqueId = User,
                    cacheBits = this.Serialize(),
                    LastWrite = DateTime.Now
                };
                //// update the DB and the lastwrite                
                db.Entry(Cache).State = Cache.EntryId == 0 ? EntityState.Added : EntityState.Modified;                
                db.SaveChanges();
                this.HasStateChanged = false;
            }
        }
        void BeforeWriteNotification(TokenCacheNotificationArgs args)
        {
            // if you want to ensure that no concurrent write take place, use this notification to place a lock on the entry
        }
    }

}

      

+5


source







All Articles