Quantcast
Channel: Randy Riness @ SPSCC aggregator
Viewing all articles
Browse latest Browse all 3015

MSDN Blogs: Using Redis as ADAL token cache

$
0
0

Here is a sample TokenCache class implementation using Redis for use with the Active Directory Access Library (ADAL). The library is used for obtaining tokens from Azure AD or AD FS using the OAuth2 protocol. This implementation is intended for web applications acting as OAuth2 or OpenIDConnect clients. Typical use of this class is in the AuthenticationContext constructor:

var authContext = new AuthenticationContext(Authority, new RedisTokenCache(signedInUserID));

Note that this is sample code, not intended for production purposes. To use it, you need to provide the Redis connection string in the application’s configuration file with the ‘CacheConnection’ string as key.

public class RedisTokenCache: TokenCache
{
   private string userId;
   private UserTokenCache Cache;
   public RedisTokenCache(string signedInUserId)
   {
      // associate the cache to the current user of the web app
      userId = signedInUserId;
      this.AfterAccess = AfterAccessNotification;
      this.BeforeAccess = BeforeAccessNotification;
      this.BeforeWrite = BeforeWriteNotification;
      // look up the entry in the cache
      var cache = Utils.Redis.Connection.GetDatabase();
      try
      {
         var cachedItem = cache.StringGet(userId);
         if (cachedItem.HasValue)
         {
             Cache = JsonConvert.DeserializeObject<UserTokenCache>(cachedItem);
             this.Deserialize((Cache == null) ? null : MachineKey.Unprotect(Cache.cacheBits, "ADALCache"));
         }
         } catch(Exception ex)
         {
             Trace.WriteLine("Exception in RedisTokenCache(id): " + ex.Message);
             Cache = null;
         }
    }

    // clean up the database
    public override void Clear()
    {
        base.Clear();
        try
        {
            var cache = Utils.Redis.Connection.GetDatabase();
            cache.KeyDelete(userId);
        } catch(Exception ex)
        {
            Trace.WriteLine("Exception in RedisTokenCache.Clear: " + ex.Message);
        }
    }

    // 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)
    {
       try
       {
           var cache = Utils.Redis.Connection.GetDatabase();
           var cachedItem = cache.StringGet(userId);
           if (cachedItem.HasValue)
           {
               var status = JsonConvert.DeserializeObject<UserTokenCache>(cachedItem);
               if ((Cache != null) && (status.LastWrite > Cache.LastWrite))
               {
                   Cache = status;
                   this.Deserialize((Cache == null) ? null : MachineKey.Unprotect(Cache.cacheBits, "ADALCache"));
               }
           }
           } catch(Exception ex)
           {
               Trace.WriteLine("Exception in RedisTokenCache.BeforeAccessNotification: " + ex.Message);
           }
        }
    }

    // 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 UserTokenCache
           {
               cacheBits = MachineKey.Protect(this.Serialize(), "ADALCache"),
               LastWrite = DateTime.Now
           };
           // update the Redis cache and the lastwrite
           try
           {
               var cache = Utils.Redis.Connection.GetDatabase();
               var cacheItemJson = JsonConvert.SerializeObject(Cache);
               cache.StringSet(userId, cacheItemJson, TimeSpan.FromDays(1)); // could we use token expiry somehow?
           }
           catch (Exception ex)
           {
               Trace.WriteLine("Exception in RedisTokenCache.AfterAccessNotification: " + ex.Message);
           }
           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
    }

    public override void DeleteItem(TokenCacheItem item)
    {
        base.DeleteItem(item);
        try
        {
            var cache = Utils.Redis.Connection.GetDatabase();
            var cachedItem = cache.KeyDelete(userId);
        }
        catch (Exception ex)
        {
            Trace.WriteLine("Exception in RedisTokenCache.DeleteItem: " + ex.Message);
        }
    }
}

public class Redis
{
    // Redis Connection string info
    private static Lazy lazyConnection = new Lazy(() =>
    {
        string cacheConnection = ConfigurationManager.AppSettings["CacheConnection"].ToString();
        return ConnectionMultiplexer.Connect(cacheConnection);

    });

    public static ConnectionMultiplexer Connection
    {
        get
        {
            return lazyConnection.Value;
        }
    }
}

 


Viewing all articles
Browse latest Browse all 3015

Latest Images

Trending Articles



Latest Images