/// <summary>
        /// Notification that is triggered after token acquisition.
        /// </summary>
        /// <param name="args">Arguments related to the cache item impacted</param>
        public override void AfterAccessNotification(TokenCacheNotificationArgs args)
        {
            MsalCacheStorage cacheStorage = GetMsalCacheStorage();

            args.AssertNotNull(nameof(args));

            try
            {
                if (args.HasStateChanged)
                {
                    cacheStorage.WriteData(args.TokenCache.SerializeMsalV3());
                }
            }
            catch (Exception)
            {
                cacheStorage.Clear();
                throw;
            }
            finally
            {
                CrossPlatformLock localDispose = cacheLock;
                cacheLock = null;
                localDispose?.Dispose();
            }
        }
Example #2
0
        public void ImportExport_ThrowException()
        {
            // Arrange
            var cacheAccessor = NSubstitute.Substitute.For <ICacheAccessor>();
            var cache         = new MockTokenCache();
            var storage       = new MsalCacheStorage(
                _storageCreationPropertiesBuilder.Build(),
                cacheAccessor,
                new TraceSourceLogger(new TraceSource("ts")));
            var helper = new MsalCacheHelper(cache, storage, _logger);

            byte[] dataToSave = Encoding.UTF8.GetBytes("Hello World 2");
            var    ex         = new InvalidCastException();

            cacheAccessor.Read().Throws(ex);

            // Act
            var actualEx = AssertException.Throws <InvalidCastException>(
                () => helper.LoadUnencryptedTokenCache());

            // Assert
            Assert.AreEqual(ex, actualEx);

            // Arrange
            cacheAccessor.WhenForAnyArgs(c => c.Write(default)).Throw(ex);
        /// <inheritdoc />
        public SharedTokenCacheProvider(IConfiguration config = null, ILogger logger = null)
        {
            _logger = logger;
            _config = config ?? new ConfigurationBuilder().AddEnvironmentVariables().Build();

            const string serviceName = "Microsoft.Developer.IdentityService";
            const string clientId    = "04b07795-8ddb-461a-bbee-02f9e1bf7b46";
            var          storageCreationPropertiesBuilder = new StorageCreationPropertiesBuilder(
                Path.GetFileName(s_cacheFilePath),
                Path.GetDirectoryName(s_cacheFilePath),
                clientId)
                                                            .WithMacKeyChain(serviceName: serviceName, accountName: "MSALCache")
                                                            .WithLinuxKeyring(
                schemaName: "msal.cache",
                collection: "default",
                secretLabel: "MSALCache",
                attribute1: new KeyValuePair <string, string>("MsalClientID", serviceName),
                attribute2: new KeyValuePair <string, string>("MsalClientVersion", "1.0.0.0"));

            var authority = string.Format(CultureInfo.InvariantCulture,
                                          AadAuthority.AadCanonicalAuthorityTemplate,
                                          AadAuthority.DefaultTrustedHost,
                                          "common");

            _app = PublicClientApplicationBuilder
                   .Create(clientId)
                   .WithAuthority(new Uri(authority))
                   .Build();

            var cacheStore = new MsalCacheStorage(storageCreationPropertiesBuilder.Build());

            _cacheHelper = new MsalCacheHelper(_app.UserTokenCache, cacheStore);
            _cacheHelper.RegisterCache(_app.UserTokenCache);
        }
        public void MsalTestClear()
        {
            var store = new MsalCacheStorage(s_storageCreationProperties, logger: _logger);

            Assert.IsTrue(store.HasChanged);
            var tempData = store.ReadData();

            Assert.IsFalse(store.HasChanged);

            var store2 = new MsalCacheStorage(s_storageCreationProperties, logger: _logger);

            Assert.IsNotNull(Exception <ArgumentNullException>(() => store.WriteData(null)));

            byte[] data = { 2, 2, 3 };
            store.WriteData(data);

            Assert.IsFalse(store.HasChanged);
            Assert.IsTrue(store2.HasChanged);

            store2.ReadData();

            Enumerable.SequenceEqual(store.ReadData(), data);
            Assert.IsTrue(File.Exists(CacheFilePath));

            store.Clear();
            Assert.IsFalse(store.HasChanged);
            Assert.IsTrue(store2.HasChanged);

            Assert.IsFalse(store.ReadData().Any());
            Assert.IsFalse(store2.ReadData().Any());
            Assert.IsFalse(File.Exists(CacheFilePath));
        }
        public void MsalNewStoreNoFile()
        {
            var store = new MsalCacheStorage(s_storageCreationProperties, logger: _logger);

            Assert.IsFalse(store.HasChanged);
            Assert.IsFalse(store.ReadData().Any());
        }
        public void CacheStorageFactory_WithFallback_Linux()
        {
            var storageWithKeyRing = new StorageCreationPropertiesBuilder(
                Path.GetFileName(CacheFilePath),
                Path.GetDirectoryName(CacheFilePath),
                "ClientIDGoesHere")
                                     .WithMacKeyChain(serviceName: "Microsoft.Developer.IdentityService", accountName: "MSALCache")
                                     .WithLinuxKeyring(
                schemaName: "msal.cache",
                collection: "default",
                secretLabel: "MSALCache",
                attribute1: new KeyValuePair <string, string>("MsalClientID", "Microsoft.Developer.IdentityService"),
                attribute2: new KeyValuePair <string, string>("MsalClientVersion", "1.0.0.0"))
                                     .Build();

            // Tests run on machines without Libsecret
            MsalCacheStorage store = MsalCacheStorage.Create(storageWithKeyRing, logger: _logger);

            Assert.IsTrue(store.CacheAccessor is LinuxKeyringAccessor);

            // ADO Linux test agents do not have libsecret installed by default
            // If you run this test on a Linux box with UI / LibSecret, then this test will fail
            // because the statement below will not throw.
            AssertException.Throws <MsalCachePersistenceException>(
                () => store.VerifyPersistence());

            store = MsalCacheStorage.Create(s_storageCreationProperties, _logger);
            Assert.IsTrue(store.CacheAccessor is FileAccessor);

            store.VerifyPersistence();
        }
        public void MsalWriteEmptyData()
        {
            var store = MsalCacheStorage.Create(s_storageCreationProperties, logger: _logger);

            Assert.ThrowsException <ArgumentNullException>(() => store.WriteData(null));

            store.WriteData(new byte[0]);

            Assert.IsFalse(store.ReadData().Any());
        }
        public void CacheStorageFactoryMac()
        {
            MsalCacheStorage store = MsalCacheStorage.Create(s_storageCreationProperties, logger: _logger);

            Assert.IsTrue(store.CacheAccessor is MacKeychainAccessor);
            store.VerifyPersistence();

            store = MsalCacheStorage.Create(s_storageCreationProperties, logger: _logger);
            Assert.IsTrue(store.CacheAccessor is MacKeychainAccessor);
        }
        public void CacheStorageFactoryWindows()
        {
            MsalCacheStorage store = MsalCacheStorage.Create(s_storageCreationProperties, logger: _logger);

            Assert.IsTrue(store.CacheAccessor is DpApiEncryptedFileAccessor);
            store.VerifyPersistence();

            store = MsalCacheStorage.Create(s_storageCreationProperties, logger: _logger);
            Assert.IsTrue(store.CacheAccessor is DpApiEncryptedFileAccessor);
        }
        /// <summary>
        /// Gets an aptly configured instance of the <see cref="MsalCacheStorage" /> class.
        /// </summary>
        /// <returns>An aptly configured instance of the <see cref="MsalCacheStorage" /> class.</returns>
        private MsalCacheStorage GetMsalCacheStorage()
        {
            StorageCreationPropertiesBuilder builder = new StorageCreationPropertiesBuilder(Path.GetFileName(CacheFilePath), Path.GetDirectoryName(CacheFilePath), ClientId);

            builder = builder.WithMacKeyChain(serviceName: "Microsoft.Developer.IdentityService", accountName: "MSALCache");
            builder = builder.WithLinuxKeyring(
                schemaName: "msal.cache",
                collection: "default",
                secretLabel: "MSALCache",
                attribute1: new KeyValuePair <string, string>("MsalClientID", "Microsoft.Developer.IdentityService"),
                attribute2: new KeyValuePair <string, string>("MsalClientVersion", "1.0.0.0"));

            return(MsalCacheStorage.Create(builder.Build(), new TraceSource("Partner Center PowerShell")));
        }
        public void CacheStorageReadCanHandleReadingNull()
        {
            // Arrange
            var cacheAccessor = NSubstitute.Substitute.For <ICacheAccessor>();

            cacheAccessor.Read().Returns((byte[])null);

            var actualLogger = new TraceSourceLogger(_logger);
            var storage      = new MsalCacheStorage(s_storageCreationProperties, cacheAccessor, actualLogger);

            // Act
            byte[] result = storage.ReadData();

            // Assert
            Assert.AreEqual(0, result.Length);
        }
        public void ClearCanThrowExceptions()
        {
            // Arrange
            var actualLogger  = new TraceSourceLogger(_logger);
            var cacheAccessor = NSubstitute.Substitute.For <ICacheAccessor>();

            cacheAccessor.WhenForAnyArgs(c => c.Clear()).Throw(new InvalidOperationException());
            var storage = new MsalCacheStorage(s_storageCreationProperties, cacheAccessor, actualLogger);

            // Act
            storage.Clear();

            // Assert
            AssertException.Throws <InvalidOperationException>(
                () => storage.Clear(ignoreExceptions: false));
        }
        public void MsalWriteGoodData()
        {
            var store = MsalCacheStorage.Create(s_storageCreationProperties, logger: _logger);

            Assert.ThrowsException <ArgumentNullException>(() => store.WriteData(null));

            byte[] data  = { 2, 2, 3 };
            byte[] data2 = { 2, 2, 3, 4, 4 };
            store.WriteData(data);
            Assert.IsTrue(Enumerable.SequenceEqual(store.ReadData(), data));

            store.WriteData(data);
            store.WriteData(data2);
            store.WriteData(data);
            store.WriteData(data2);
            Assert.IsTrue(Enumerable.SequenceEqual(store.ReadData(), data2));
        }
        public void VerifyPersistenceThrowsIfDataReadIsEmpty()
        {
            // Arrange
            var actualLogger  = new TraceSourceLogger(_logger);
            var cacheAccessor = Substitute.For <ICacheAccessor>();

            cacheAccessor.CreateForPersistenceValidation().Returns(cacheAccessor);
            var storage = new MsalCacheStorage(s_storageCreationProperties, cacheAccessor, actualLogger);


            // Act
            var ex = AssertException.Throws <MsalCachePersistenceException>(
                () => storage.VerifyPersistence());

            // Assert
            Assert.IsNull(ex.InnerException); // no more details available
        }
        public void CacheStorageReadCanHandleExceptionsWhenReading()
        {
            // Arrange
            var cacheAccessor = NSubstitute.Substitute.For <ICacheAccessor>();
            var exception     = new InvalidOperationException();

            cacheAccessor.Read().Throws(exception);

            var actualLogger = new TraceSourceLogger(_logger);
            var storage      = new MsalCacheStorage(s_storageCreationProperties, cacheAccessor, actualLogger);

            // Act
            byte[] result = storage.ReadData();

            // Assert
            Assert.AreEqual(0, result.Length);
        }
        public void ReadCanThrowExceptions()
        {
            // Arrange
            var actualLogger  = new TraceSourceLogger(_logger);
            var cacheAccessor = NSubstitute.Substitute.For <ICacheAccessor>();

            cacheAccessor.Read().Throws(new InvalidOperationException());
            var storage = new MsalCacheStorage(s_storageCreationProperties, cacheAccessor, actualLogger);

            // Act
            byte[] result = storage.ReadData();
            Assert.AreEqual(0, result.Length);

            // Assert
            AssertException.Throws <InvalidOperationException>(
                () => storage.ReadData(ignoreExceptions: false));
        }
        public void CacheStorageFactory_WithFallback_Linux()
        {
            var storageWithKeyRing = new StorageCreationPropertiesBuilder(
                Path.GetFileName(CacheFilePath),
                Path.GetDirectoryName(CacheFilePath),
                "ClientIDGoesHere")
                                     .WithMacKeyChain(serviceName: "Microsoft.Developer.IdentityService", accountName: "MSALCache")
                                     .WithLinuxKeyring(
                schemaName: "msal.cache",
                collection: "default",
                secretLabel: "MSALCache",
                attribute1: new KeyValuePair <string, string>("MsalClientID", "Microsoft.Developer.IdentityService"),
                attribute2: new KeyValuePair <string, string>("MsalClientVersion", "1.0.0.0"))
                                     .Build();

            // Tests run on machines without Libsecret
            MsalCacheStorage store = MsalCacheStorage.Create(storageWithKeyRing, logger: _logger);

            Assert.IsTrue(store.CacheAccessor is LinuxKeyringAccessor);

            // ADO Linux test agents do not have libsecret installed by default
            // If you run this test on a Linux box with UI / LibSecret, then this test will fail
            // because the statement below will not throw.
            AssertException.Throws <MsalCachePersistenceException>(
                () => store.VerifyPersistence());

            MsalCacheStorage unprotectedStore = MsalCacheStorage.Create(s_storageCreationProperties, _logger);

            Assert.IsTrue(unprotectedStore.CacheAccessor is FileAccessor);

            unprotectedStore.VerifyPersistence();

            unprotectedStore.WriteData(new byte[] { 2, 3 });

            // Unproteced cache file should exist
            Assert.IsTrue(File.Exists(unprotectedStore.CacheFilePath));

            // Mimic another sdk client to check libsecret availability by calling
            // MsalCacheStorage.VerifyPeristence() -> LinuxKeyringAccessor.CreateForPersistenceValidation()
            AssertException.Throws <MsalCachePersistenceException>(
                () => store.VerifyPersistence());

            // Verify above call doesn't delete existing cache file
            Assert.IsTrue(File.Exists(unprotectedStore.CacheFilePath));
        }
        public void VerifyPersistenceThrowsInnerExceptions()
        {
            // Arrange
            var actualLogger  = new TraceSourceLogger(_logger);
            var cacheAccessor = Substitute.For <ICacheAccessor>();

            cacheAccessor.CreateForPersistenceValidation().Returns(cacheAccessor);
            var exception = new InvalidOperationException("some error");
            var storage   = new MsalCacheStorage(s_storageCreationProperties, cacheAccessor, actualLogger);

            cacheAccessor.Read().Throws(exception);

            // Act
            var ex = AssertException.Throws <MsalCachePersistenceException>(
                () => storage.VerifyPersistence());

            // Assert
            Assert.AreEqual(ex.InnerException, exception);
        }
        public void VerifyPersistenceThrowsIfDataReadIsDiffrentFromDataWritten()
        {
            // Arrange
            var stringListener = new TraceStringListener();
            var actualLogger   = new TraceSourceLogger(_logger);
            var cacheAccessor  = Substitute.For <ICacheAccessor>();

            cacheAccessor.CreateForPersistenceValidation().Returns(cacheAccessor);
            var storage = new MsalCacheStorage(s_storageCreationProperties, cacheAccessor, actualLogger);

            cacheAccessor.Read().Returns(Encoding.UTF8.GetBytes("other_dummy_data"));

            // Act
            var ex = AssertException.Throws <MsalCachePersistenceException>(
                () => storage.VerifyPersistence());

            // Assert
            Assert.IsNull(ex.InnerException); // no more details available
        }
        /// <summary>
        /// Notification that is triggered before token acquisition.
        /// </summary>
        /// <param name="args">Arguments related to the cache item impacted</param>
        public override void BeforeAccessNotification(TokenCacheNotificationArgs args)
        {
            MsalCacheStorage cacheStorage = GetMsalCacheStorage();

            args.AssertNotNull(nameof(args));

            try
            {
                cacheLock = new CrossPlatformLock($"{CacheFilePath}.lockfile");

                cacheLock.CreateLockAsync().ConfigureAwait(false);
                args.TokenCache.DeserializeMsalV3(cacheStorage.ReadData());
            }
            catch (Exception)
            {
                cacheStorage.Clear();
                throw;
            }
        }
Example #21
0
        internal SharedTokenCacheProvider(StorageCreationPropertiesBuilder builder, IConfiguration config = null, ILogger logger = null)
        {
            _logger = logger;
            _config = config ?? new ConfigurationBuilder().AddEnvironmentVariables().Build();

            var authority = _config.GetValue <string>(Constants.AadAuthorityEnvName) ??
                            string.Format(CultureInfo.InvariantCulture,
                                          AadAuthority.AadCanonicalAuthorityTemplate,
                                          AadAuthority.DefaultTrustedHost,
                                          "common");

            _app = PublicClientApplicationBuilder
                   .Create(AzureCliClientId)
                   .WithAuthority(new Uri(authority))
                   .Build();

            var cacheStore = new MsalCacheStorage(builder.Build());

            _cacheHelper = new MsalCacheHelper(_app.UserTokenCache, cacheStore);
            _cacheHelper.RegisterCache(_app.UserTokenCache);
        }
        public void CacheStorageCanHandleMultipleExceptionsWhenReading()
        {
            // Arrange
            var stringListener = new TraceStringListener();
            var cacheAccessor  = Substitute.For <ICacheAccessor>();
            var exception      = new InvalidOperationException("some error");

            cacheAccessor.Read().Throws(exception);
            cacheAccessor.When((x) => x.Clear()).Do(x => throw exception);
            _logger.Listeners.Add(stringListener);
            var actualLogger = new TraceSourceLogger(_logger);
            var storage      = new MsalCacheStorage(s_storageCreationProperties, cacheAccessor, actualLogger);

            // Act
            byte[] result = storage.ReadData();

            // Assert
            Assert.AreEqual(0, result.Length);
            Assert.IsTrue(stringListener.CurrentLog.Contains("TestSource Error"));
            Assert.IsTrue(stringListener.CurrentLog.Contains("InvalidOperationException"));
            Assert.IsTrue(stringListener.CurrentLog.Contains("some error"));
        }
Example #23
0
        public void ImportExport()
        {
            // Arrange
            var cacheAccessor = NSubstitute.Substitute.For <ICacheAccessor>();
            var cache         = new MockTokenCache();
            var storage       = new MsalCacheStorage(
                _storageCreationPropertiesBuilder.Build(),
                cacheAccessor,
                new TraceSourceLogger(new TraceSource("ts")));
            var helper = new MsalCacheHelper(cache, storage, _logger);

            byte[] dataToSave = Encoding.UTF8.GetBytes("Hello World 2");

            cacheAccessor.Read().Returns(Encoding.UTF8.GetBytes("Hello World"));

            // Act
            byte[] actualData = helper.LoadUnencryptedTokenCache();
            helper.SaveUnencryptedTokenCache(dataToSave);

            // Assert
            Assert.AreEqual("Hello World", Encoding.UTF8.GetString(actualData));
            cacheAccessor.Received().Write(dataToSave);
        }
        public void VerifyPersistenceHappyPath()
        {
            // Arrange
            byte[] dummyData     = Encoding.UTF8.GetBytes(MsalCacheStorage.PersistenceValidationDummyData);
            var    actualLogger  = new TraceSourceLogger(_logger);
            var    cacheAccessor = Substitute.For <ICacheAccessor>();

            cacheAccessor.CreateForPersistenceValidation().Returns(cacheAccessor);
            var storage = new MsalCacheStorage(s_storageCreationProperties, cacheAccessor, actualLogger);

            cacheAccessor.Read().Returns(dummyData);

            // Act
            storage.VerifyPersistence();

            // Assert
            Received.InOrder(() => {
                cacheAccessor.CreateForPersistenceValidation();
                cacheAccessor.Write(Arg.Any <byte[]>());
                cacheAccessor.Read();
                cacheAccessor.Clear();
            });
        }
        public void MsalTestClear()
        {
            var store = MsalCacheStorage.Create(s_storageCreationProperties, logger: _logger);

            store.ReadData();

            var store2 = MsalCacheStorage.Create(s_storageCreationProperties, logger: _logger);

            AssertException.Throws <ArgumentNullException>(() => store.WriteData(null));

            byte[] data = { 2, 2, 3 };
            store.WriteData(data);
            store2.ReadData();

            Assert.IsTrue(Enumerable.SequenceEqual(store.ReadData(), data));
            Assert.IsTrue(File.Exists(CacheFilePath));

            store.Clear();

            Assert.IsFalse(store.ReadData().Any());
            Assert.IsFalse(store2.ReadData().Any());
            Assert.IsFalse(File.Exists(CacheFilePath));
        }
        private void CleanTestData()
        {
            var store = MsalCacheStorage.Create(s_storageCreationProperties, logger: _logger);

            store.Clear();
        }