Skip to content

Instantly share code, notes, and snippets.

Created August 16, 2016 15:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anonymous/8bb5319fb0b5297f209913ce77bd1f56 to your computer and use it in GitHub Desktop.
Save anonymous/8bb5319fb0b5297f209913ce77bd1f56 to your computer and use it in GitHub Desktop.
class FileCache : TokenCache
{
private const DataProtectionScope DpScope = DataProtectionScope.CurrentUser;
private static readonly byte[] DpEntropy = Convert.FromBase64String("SmuJ3fxsOxitDcR+51332A==");
private const string FileLockPrefix = "9384fef8-0a3e-4bad-8c5e-8d1001a80aba";
private readonly object _fileLock;
private readonly FileInfo _file;
public FileCache(string filePath = @".\TokenCache.dat")
{
_file = new FileInfo(filePath);
AfterAccess = AfterAccessNotification;
BeforeAccess = BeforeAccessNotification;
_fileLock = string.Intern(FileLockPrefix + _file.FullName.ToLowerInvariant());
lock (_fileLock)
{
ReadFile();
}
}
public override void Clear()
{
lock (_fileLock)
{
_file.Delete();
}
}
void BeforeAccessNotification(TokenCacheNotificationArgs args)
{
lock (_fileLock)
{
ReadFile();
}
}
void AfterAccessNotification(TokenCacheNotificationArgs args)
{
if (!HasStateChanged)
return;
lock (_fileLock)
{
if (!HasStateChanged)
return;
WriteFile();
HasStateChanged = false;
}
}
private void ReadFile()
{
try
{
var cipherText = ReadAllBytes(_file.FullName);
var clearText = Unprotect(cipherText, DpEntropy, DpScope);
Deserialize(clearText);
}
catch
{
Deserialize(null);
}
}
private void WriteFile()
{
var clearText = Serialize();
var cipherText = Protect(clearText, DpEntropy, DpScope);
WriteAllBytes(_file.FullName, cipherText);
}
}
@ronnieoverby
Copy link

ronnieoverby commented Aug 16, 2016

Improvements:

  • Lock scoped at file path (pluralsight example used a static lock accessed by instance methods)
  • Lock contended for at surface level of call stack (this pattern leads to deadlocks less often)
  • Double-checked locking at call site for writing to file
  • DPAPI entropy provided.
  • Removed file exists race condition.
  • Lock when deleting file.

@ronnieoverby
Copy link

oops forgot to include usings:

using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System;
using System.IO;
using System.Security.Cryptography;
using static System.IO.File;
using static System.Security.Cryptography.ProtectedData;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment