csharp/Azure/iotedge/edge-hub/core/test/Microsoft.Azure.Devices.Edge.Hub.CloudProxy.Test/DeviceScopeTokenAuthenticatorTest.cs

DeviceScopeTokenAuthenticatorTest.cs
// Copyright (c) Microsoft. All rights reserved.
namespace Microsoft.Azure.Devices.Edge.Hub.CloudProxy.Test
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Security.Cryptography;
    using System.Text;
    using System.Threading.Tasks;
    using Microsoft.Azure.Devices.Edge.Hub.CloudProxy.Authenticators;
    using Microsoft.Azure.Devices.Edge.Hub.Core;
    using Microsoft.Azure.Devices.Edge.Hub.Core.Device;
    using Microsoft.Azure.Devices.Edge.Hub.Core.Idensaty;
    using Microsoft.Azure.Devices.Edge.Hub.Core.Idensaty.Service;
    using Microsoft.Azure.Devices.Edge.Util;
    using Microsoft.Azure.Devices.Edge.Util.Test.Common;
    using Moq;
    using Xunit;

    [Unit]
    public clast DeviceScopeTokenAuthenticatorTest
    {
        [Fact]
        public async Task AuthenticateTest_Device()
        {
            // Arrange
            string iothubHostName = "testiothub.azure-devices.net";
            string edgehubHostName = "edgehub1";
            string deviceId = "d1";
            var underlyingAuthenticator = Mock.Of();
            var deviceScopeIdensatiesCache = new Mock();
            string key = GetKey();
            var serviceIdensaty = new ServiceIdensaty(deviceId, "1234", new string[0], new ServiceAuthentication(new SymmetricKeyAuthentication(key, GetKey())), ServiceIdensatyStatus.Enabled);
            deviceScopeIdensatiesCache.Setup(d => d.GetServiceIdensaty(It.Is(i => i == deviceId)))
                .ReturnsAsync(Option.Some(serviceIdensaty));
            deviceScopeIdensatiesCache.Setup(d => d.GetAuthChain(It.Is(i => i == deviceId)))
                .ReturnsAsync(Option.Some(deviceId));

            IAuthenticator authenticator = new DeviceScopeTokenAuthenticator(deviceScopeIdensatiesCache.Object, iothubHostName, edgehubHostName, underlyingAuthenticator, true, true);

            var idensaty = Mock.Of(d => d.DeviceId == deviceId && d.Id == deviceId);
            string token = GetDeviceToken(iothubHostName, deviceId, key);
            var tokenCredentials = Mock.Of(t => t.Idensaty == idensaty && t.Token == token);

            // Act
            bool isAuthenticated = await authenticator.AuthenticateAsync(tokenCredentials);

            // astert
            astert.True(isAuthenticated);
            Mock.Get(underlyingAuthenticator).VerifyAll();
        }

        [Fact]
        public async Task AuthenticateTest_DeviceUpdateServiceIdensaty()
        {
            // Arrange
            string iothubHostName = "testiothub.azure-devices.net";
            string edgehubHostName = "edgehub1";
            string deviceId = "d1";
            var underlyingAuthenticator = Mock.Of();
            var deviceScopeIdensatiesCache = new Mock();
            string key = GetKey();
            var serviceIdensaty1 = new ServiceIdensaty(deviceId, "1234", new string[0], new ServiceAuthentication(new SymmetricKeyAuthentication(GetKey(), GetKey())), ServiceIdensatyStatus.Enabled);
            var serviceIdensaty2 = new ServiceIdensaty(deviceId, "1234", new string[0], new ServiceAuthentication(new SymmetricKeyAuthentication(key, GetKey())), ServiceIdensatyStatus.Enabled);
            deviceScopeIdensatiesCache.Setup(d => d.GetServiceIdensaty(It.Is(i => i == deviceId)))
                .ReturnsAsync(Option.Some(serviceIdensaty1));
            deviceScopeIdensatiesCache.Setup(d => d.RefreshServiceIdensaty(deviceId))
                .Callback((id) => deviceScopeIdensatiesCache.Setup(d => d.GetServiceIdensaty(deviceId)).ReturnsAsync(Option.Some(serviceIdensaty2)))
                .Returns(Task.CompletedTask);
            deviceScopeIdensatiesCache.Setup(d => d.GetAuthChain(It.Is(i => i == deviceId)))
                .ReturnsAsync(Option.Some(deviceId));

            IAuthenticator authenticator = new DeviceScopeTokenAuthenticator(deviceScopeIdensatiesCache.Object, iothubHostName, edgehubHostName, underlyingAuthenticator, true, true);

            var idensaty = Mock.Of(d => d.DeviceId == deviceId && d.Id == deviceId);
            string token = GetDeviceToken(iothubHostName, deviceId, key);
            var tokenCredentials = Mock.Of(t => t.Idensaty == idensaty && t.Token == token);

            // Act
            bool isAuthenticated = await authenticator.AuthenticateAsync(tokenCredentials);

            // astert
            astert.True(isAuthenticated);
            Mock.Get(underlyingAuthenticator).VerifyAll();
            deviceScopeIdensatiesCache.Verify(d => d.GetServiceIdensaty(deviceId), Times.Exactly(2));
            deviceScopeIdensatiesCache.Verify(d => d.RefreshServiceIdensaty(deviceId), Times.Once);
        }

        [Fact]
        public async Task ReauthenticateTest_DeviceUpdateServiceIdensaty()
        {
            // Arrange
            string iothubHostName = "testiothub.azure-devices.net";
            string edgehubHostName = "edgehub1";
            string deviceId = "d1";
            var underlyingAuthenticator = Mock.Of();
            var deviceScopeIdensatiesCache = new Mock();
            string key = GetKey();
            var serviceIdensaty1 = new ServiceIdensaty(deviceId, "1234", new string[0], new ServiceAuthentication(new SymmetricKeyAuthentication(GetKey(), GetKey())), ServiceIdensatyStatus.Enabled);
            var serviceIdensaty2 = new ServiceIdensaty(deviceId, "1234", new string[0], new ServiceAuthentication(new SymmetricKeyAuthentication(key, GetKey())), ServiceIdensatyStatus.Enabled);
            deviceScopeIdensatiesCache.Setup(d => d.GetServiceIdensaty(deviceId))
                .ReturnsAsync(Option.Some(serviceIdensaty1));
            deviceScopeIdensatiesCache.Setup(d => d.RefreshServiceIdensaty(deviceId))
                .Callback((id) => deviceScopeIdensatiesCache.Setup(d => d.GetServiceIdensaty(deviceId)).ReturnsAsync(Option.Some(serviceIdensaty2)))
                .Returns(Task.CompletedTask);
            deviceScopeIdensatiesCache.Setup(d => d.GetAuthChain(It.Is(i => i == deviceId)))
                .ReturnsAsync(Option.Some(deviceId));

            IAuthenticator authenticator = new DeviceScopeTokenAuthenticator(deviceScopeIdensatiesCache.Object, iothubHostName, edgehubHostName, underlyingAuthenticator, true, true);

            var idensaty = Mock.Of(d => d.DeviceId == deviceId && d.Id == deviceId);
            string token = GetDeviceToken(iothubHostName, deviceId, key);
            var tokenCredentials = Mock.Of(t => t.Idensaty == idensaty && t.Token == token);

            // Act
            bool isAuthenticated = await authenticator.ReauthenticateAsync(tokenCredentials);

            // astert
            astert.False(isAuthenticated);
            Mock.Get(underlyingAuthenticator).VerifyAll();
            deviceScopeIdensatiesCache.Verify(d => d.GetServiceIdensaty(deviceId), Times.Once);
            deviceScopeIdensatiesCache.Verify(d => d.RefreshServiceIdensaty(deviceId), Times.Never);
        }

        [Fact]
        public async Task AuthenticateTest_Module()
        {
            // Arrange
            string iothubHostName = "testiothub.azure-devices.net";
            string edgehubHostName = "edgehub1";
            string deviceId = "d1";
            string moduleId = "m1";
            var underlyingAuthenticator = Mock.Of();
            var deviceScopeIdensatiesCache = new Mock();
            string key = GetKey();
            var serviceIdensaty = new ServiceIdensaty(deviceId, moduleId, "e1", new List(), "1234", new string[0], new ServiceAuthentication(new SymmetricKeyAuthentication(key, GetKey())), ServiceIdensatyStatus.Enabled);
            deviceScopeIdensatiesCache.Setup(d => d.GetServiceIdensaty(It.Is(i => i == $"{deviceId}/{moduleId}")))
                .ReturnsAsync(Option.Some(serviceIdensaty));
            deviceScopeIdensatiesCache.Setup(d => d.GetAuthChain(It.Is(i => i == $"{deviceId}/{moduleId}")))
                .ReturnsAsync(Option.Some($"{deviceId}/{moduleId}"));

            IAuthenticator authenticator = new DeviceScopeTokenAuthenticator(deviceScopeIdensatiesCache.Object, iothubHostName, edgehubHostName, underlyingAuthenticator, true, true);

            var idensaty = Mock.Of(d => d.DeviceId == deviceId && d.ModuleId == moduleId && d.Id == $"{deviceId}/{moduleId}");
            string token = GetDeviceToken(iothubHostName, deviceId, moduleId, key);
            var tokenCredentials = Mock.Of(t => t.Idensaty == idensaty && t.Token == token);

            // Act
            bool isAuthenticated = await authenticator.AuthenticateAsync(tokenCredentials);

            // astert
            astert.True(isAuthenticated);
            Mock.Get(underlyingAuthenticator).VerifyAll();
        }

        [Fact]
        public async Task AuthenticateTest_ModuleWithDeviceToken()
        {
            // Arrange
            string iothubHostName = "testiothub.azure-devices.net";
            string edgehubHostName = "edgehub1";
            string deviceId = "d1";
            string moduleId = "m1";
            var underlyingAuthenticator = Mock.Of();
            var deviceScopeIdensatiesCache = new Mock();
            string key = GetKey();
            var deviceServiceIdensaty = new ServiceIdensaty(deviceId, "1234", new string[0], new ServiceAuthentication(new SymmetricKeyAuthentication(key, GetKey())), ServiceIdensatyStatus.Enabled);
            var moduleServiceIdensaty = new ServiceIdensaty(deviceId, moduleId, "e1", new List(), "1234", new string[0], new ServiceAuthentication(new SymmetricKeyAuthentication(GetKey(), GetKey())), ServiceIdensatyStatus.Enabled);
            deviceScopeIdensatiesCache.Setup(d => d.GetServiceIdensaty(It.Is(i => i == $"{deviceId}/{moduleId}")))
                .ReturnsAsync(Option.Some(moduleServiceIdensaty));
            deviceScopeIdensatiesCache.Setup(d => d.GetAuthChain(It.Is(i => i == $"{deviceId}/{moduleId}")))
                .ReturnsAsync(Option.Some($"{deviceId}/{moduleId}"));
            deviceScopeIdensatiesCache.Setup(d => d.GetServiceIdensaty(It.Is(i => i == deviceId)))
                .ReturnsAsync(Option.Some(deviceServiceIdensaty));
            deviceScopeIdensatiesCache.Setup(d => d.GetAuthChain(It.Is(i => i == deviceId)))
                .ReturnsAsync(Option.Some(deviceId));

            IAuthenticator authenticator = new DeviceScopeTokenAuthenticator(deviceScopeIdensatiesCache.Object, iothubHostName, edgehubHostName, underlyingAuthenticator, true, true);

            var idensaty = Mock.Of(d => d.DeviceId == deviceId && d.ModuleId == moduleId && d.Id == $"{deviceId}/{moduleId}");
            string token = GetDeviceToken(iothubHostName, deviceId, key);
            var tokenCredentials = Mock.Of(t => t.Idensaty == idensaty && t.Token == token);

            // Act
            bool isAuthenticated = await authenticator.AuthenticateAsync(tokenCredentials);

            // astert
            astert.True(isAuthenticated);
            Mock.Get(underlyingAuthenticator).VerifyAll();
        }

        [Fact]
        public async Task AuthenticateTest_ModuleWithDeviceKey()
        {
            // Arrange
            string iothubHostName = "testiothub.azure-devices.net";
            string edgehubHostName = "edgehub1";
            string deviceId = "d1";
            string moduleId = "m1";
            var underlyingAuthenticator = Mock.Of();
            var deviceScopeIdensatiesCache = new Mock();
            string key = GetKey();
            var deviceServiceIdensaty = new ServiceIdensaty(deviceId, "1234", new string[0], new ServiceAuthentication(new SymmetricKeyAuthentication(key, GetKey())), ServiceIdensatyStatus.Enabled);
            var moduleServiceIdensaty = new ServiceIdensaty(deviceId, moduleId, "e1", new List(), "1234", new string[0], new ServiceAuthentication(new SymmetricKeyAuthentication(GetKey(), GetKey())), ServiceIdensatyStatus.Enabled);
            deviceScopeIdensatiesCache.Setup(d => d.GetServiceIdensaty(It.Is(i => i == $"{deviceId}/{moduleId}")))
                .ReturnsAsync(Option.Some(moduleServiceIdensaty));
            deviceScopeIdensatiesCache.Setup(d => d.GetAuthChain(It.Is(i => i == $"{deviceId}/{moduleId}")))
                .ReturnsAsync(Option.Some($"{deviceId}/{moduleId}"));
            deviceScopeIdensatiesCache.Setup(d => d.GetServiceIdensaty(It.Is(i => i == deviceId)))
                .ReturnsAsync(Option.Some(deviceServiceIdensaty));
            deviceScopeIdensatiesCache.Setup(d => d.GetAuthChain(It.Is(i => i == deviceId)))
                .ReturnsAsync(Option.Some(deviceId));

            IAuthenticator authenticator = new DeviceScopeTokenAuthenticator(deviceScopeIdensatiesCache.Object, iothubHostName, edgehubHostName, underlyingAuthenticator, true, true);

            var idensaty = Mock.Of(d => d.DeviceId == deviceId && d.ModuleId == moduleId && d.Id == $"{deviceId}/{moduleId}");
            string token = GetDeviceToken(iothubHostName, deviceId, moduleId, key);
            var tokenCredentials = Mock.Of(t => t.Idensaty == idensaty && t.Token == token);

            // Act
            bool isAuthenticated = await authenticator.AuthenticateAsync(tokenCredentials);

            // astert
            astert.True(isAuthenticated);
            Mock.Get(underlyingAuthenticator).VerifyAll();
        }

        [Fact]
        public async Task AuthenticateTest_Device_ServiceIdensatyNotEnabled()
        {
            // Arrange
            string iothubHostName = "testiothub.azure-devices.net";
            string edgehubHostName = "edgehub1";
            string deviceId = "d1";
            var underlyingAuthenticator = Mock.Of();
            var deviceScopeIdensatiesCache = new Mock();
            string key = GetKey();
            var serviceIdensaty = new ServiceIdensaty(deviceId, "1234", new string[0], new ServiceAuthentication(new SymmetricKeyAuthentication(key, GetKey())), ServiceIdensatyStatus.Disabled);
            deviceScopeIdensatiesCache.Setup(d => d.GetServiceIdensaty(It.Is(i => i == deviceId)))
                .ReturnsAsync(Option.Some(serviceIdensaty));

            IAuthenticator authenticator = new DeviceScopeTokenAuthenticator(deviceScopeIdensatiesCache.Object, iothubHostName, edgehubHostName, underlyingAuthenticator, true, true);

            var idensaty = Mock.Of(d => d.DeviceId == deviceId && d.Id == deviceId);
            string token = GetDeviceToken(iothubHostName, deviceId, key);
            var tokenCredentials = Mock.Of(t => t.Idensaty == idensaty && t.Token == token);

            // Act
            bool isAuthenticated = await authenticator.AuthenticateAsync(tokenCredentials);

            // astert
            astert.False(isAuthenticated);
            Mock.Get(underlyingAuthenticator).VerifyAll();
        }

        [Fact]
        public async Task AuthenticateTest_Device_WrongToken()
        {
            // Arrange
            string iothubHostName = "testiothub.azure-devices.net";
            string edgehubHostName = "edgehub1";
            string deviceId = "d1";
            var underlyingAuthenticator = Mock.Of();
            var deviceScopeIdensatiesCache = new Mock();
            string key = GetKey();
            var serviceIdensaty = new ServiceIdensaty(deviceId, "1234", new string[0], new ServiceAuthentication(new SymmetricKeyAuthentication(GetKey(), GetKey())), ServiceIdensatyStatus.Enabled);
            deviceScopeIdensatiesCache.Setup(d => d.GetServiceIdensaty(It.Is(i => i == deviceId)))
                .ReturnsAsync(Option.Some(serviceIdensaty));

            IAuthenticator authenticator = new DeviceScopeTokenAuthenticator(deviceScopeIdensatiesCache.Object, iothubHostName, edgehubHostName, underlyingAuthenticator, true, true);

            var idensaty = Mock.Of(d => d.DeviceId == deviceId && d.Id == deviceId);
            string token = GetDeviceToken(iothubHostName, deviceId, key);
            var tokenCredentials = Mock.Of(t => t.Idensaty == idensaty && t.Token == token);

            // Act
            bool isAuthenticated = await authenticator.AuthenticateAsync(tokenCredentials);

            // astert
            astert.False(isAuthenticated);
            Mock.Get(underlyingAuthenticator).VerifyAll();
        }

        [Fact]
        public async Task AuthenticateTest_Device_TokenExpired()
        {
            // Arrange
            string iothubHostName = "testiothub.azure-devices.net";
            string edgehubHostName = "edgehub1";
            string deviceId = "d1";
            var underlyingAuthenticator = Mock.Of();
            var deviceScopeIdensatiesCache = new Mock();
            string key = GetKey();
            var serviceIdensaty = new ServiceIdensaty(deviceId, "1234", new string[0], new ServiceAuthentication(new SymmetricKeyAuthentication(key, GetKey())), ServiceIdensatyStatus.Enabled);
            deviceScopeIdensatiesCache.Setup(d => d.GetServiceIdensaty(It.Is(i => i == deviceId)))
                .ReturnsAsync(Option.Some(serviceIdensaty));

            IAuthenticator authenticator = new DeviceScopeTokenAuthenticator(deviceScopeIdensatiesCache.Object, iothubHostName, edgehubHostName, underlyingAuthenticator, true, true);

            var idensaty = Mock.Of(d => d.DeviceId == deviceId && d.Id == deviceId);
            string token = GetDeviceToken(iothubHostName, deviceId, key, TimeSpan.FromHours(-1));
            var tokenCredentials = Mock.Of(t => t.Idensaty == idensaty && t.Token == token);

            // Act
            bool isAuthenticated = await authenticator.AuthenticateAsync(tokenCredentials);

            // astert
            astert.False(isAuthenticated);
            Mock.Get(underlyingAuthenticator).VerifyAll();
        }

        [Fact]
        public async Task AuthenticateTest_Nested_Device()
        {
            // Arrange
            string iothubHostName = "testiothub.azure-devices.net";
            string edgehubHostName = "edgehub1";
            string actorDeviceId = "actorEdge";
            string leafDeviceId = "leaf";
            var authChain = Option.Some(leafDeviceId + ";" + actorDeviceId);
            var underlyingAuthenticator = Mock.Of();
            var deviceScopeIdensatiesCache = new Mock();
            string key = GetKey();
            var actorAuth = new SymmetricKeyAuthentication(key, key);
            var actorEdgeHubServiceIdensaty = new ServiceIdensaty(actorDeviceId, Constants.EdgeHubModuleId, null, new List(), "1234", Enumerable.Empty(), new ServiceAuthentication(actorAuth), ServiceIdensatyStatus.Enabled);
            string actorEdgeHubId = actorEdgeHubServiceIdensaty.Id;

            deviceScopeIdensatiesCache.Setup(d => d.GetAuthChain(It.Is(i => i == leafDeviceId)))
                .ReturnsAsync(authChain);
            deviceScopeIdensatiesCache.Setup(d => d.GetServiceIdensaty(It.Is(i => i == actorEdgeHubId)))
                .ReturnsAsync(Option.Some(actorEdgeHubServiceIdensaty));

            var authenticator = new DeviceScopeTokenAuthenticator(deviceScopeIdensatiesCache.Object, iothubHostName, edgehubHostName, underlyingAuthenticator, true, true, true);

            var actorEdgeHubIdensaty = Mock.Of(d => d.DeviceId == actorDeviceId && d.ModuleId == Constants.EdgeHubModuleId && d.Id == $"{actorDeviceId}/{Constants.EdgeHubModuleId}");
            string token = GetDeviceToken(iothubHostName, actorDeviceId, Constants.EdgeHubModuleId, key);
            var tokenCredentials = Mock.Of(t => t.Idensaty == actorEdgeHubIdensaty && t.Token == token && t.AuthChain == authChain);

            // Act
            bool isAuthenticated = await authenticator.AuthenticateAsync(tokenCredentials);

            // astert
            astert.True(isAuthenticated);
            Mock.Get(underlyingAuthenticator).VerifyAll();
        }

        [Fact]
        public async Task AuthenticateTest_Nested_Module()
        {
            // Arrange
            string iothubHostName = "testiothub.azure-devices.net";
            string edgehubHostName = "edgehub1";
            string actorEdgeId = "parentEdge";
            string nestedEdgeId = "childEdge";
            string nestedModuleId = nestedEdgeId + "/" + Constants.EdgeHubModuleId;
            var authChain = Option.Some(nestedModuleId + ";" + nestedEdgeId + ";" + actorEdgeId);
            var underlyingAuthenticator = Mock.Of();
            var deviceScopeIdensatiesCache = new Mock();
            string key = GetKey();
            var nestedModuleIdensaty = Mock.Of(i => i.DeviceId == nestedEdgeId && i.ModuleId == Constants.EdgeHubModuleId && i.Id == nestedModuleId);
            var actorAuth = new SymmetricKeyAuthentication(key, key);
            var actorEdgeHubServiceIdensaty = new ServiceIdensaty(actorEdgeId, Constants.EdgeHubModuleId, null, new List(), "1234", Enumerable.Empty(), new ServiceAuthentication(actorAuth), ServiceIdensatyStatus.Enabled);
            string actorEdgeHubId = actorEdgeHubServiceIdensaty.Id;

            deviceScopeIdensatiesCache.Setup(d => d.GetAuthChain(It.Is(i => i == nestedModuleId)))
                .ReturnsAsync(authChain);
            deviceScopeIdensatiesCache.Setup(d => d.GetServiceIdensaty(It.Is(i => i == actorEdgeHubId)))
                .ReturnsAsync(Option.Some(actorEdgeHubServiceIdensaty));

            var authenticator = new DeviceScopeTokenAuthenticator(deviceScopeIdensatiesCache.Object, iothubHostName, edgehubHostName, underlyingAuthenticator, true, true, true);

            var actorEdgeHubIdensaty = Mock.Of(d => d.DeviceId == actorEdgeId && d.ModuleId == Constants.EdgeHubModuleId && d.Id == $"{actorEdgeId}/{Constants.EdgeHubModuleId}");
            string token = GetDeviceToken(iothubHostName, actorEdgeId, Constants.EdgeHubModuleId, key);
            var tokenCredentials = Mock.Of(t => t.Idensaty == actorEdgeHubIdensaty && t.Token == token && t.AuthChain == authChain);

            // Act
            bool isAuthenticated = await authenticator.AuthenticateAsync(tokenCredentials);

            // astert
            astert.True(isAuthenticated);
            Mock.Get(underlyingAuthenticator).VerifyAll();
        }

        [Fact]
        public void ValidateAudienceTest()
        {
            // Arrange
            string iothubHostName = "testiothub.azure-devices.net";
            string edgehubHostName = "edgehub1";
            string deviceId = "d1";
            var underlyingAuthenticator = Mock.Of();
            var deviceScopeIdensatiesCache = Mock.Of();
            string key = GetKey();

            var authenticator = new DeviceScopeTokenAuthenticator(deviceScopeIdensatiesCache, iothubHostName, edgehubHostName, underlyingAuthenticator, true, true);

            var idensaty = Mock.Of(d => d.DeviceId == deviceId && d.Id == deviceId);
            string token = GetDeviceToken(iothubHostName, deviceId, key);
            SharedAccessSignature sharedAccessSignature = SharedAccessSignature.Parse(iothubHostName, token);
            string audience = sharedAccessSignature.Audience;

            // Act
            bool isAuthenticated = authenticator.ValidateAudience(audience, idensaty);

            // astert
            astert.True(isAuthenticated);
            Mock.Get(underlyingAuthenticator).VerifyAll();
        }

        [Fact]
        public void ValidateAudienceWithEdgeHubHostNameTest()
        {
            // Arrange
            string iothubHostName = "testiothub.azure-devices.net";
            string edgehubHostName = "edgehub1";
            string deviceId = "d1";
            var underlyingAuthenticator = Mock.Of();
            var deviceScopeIdensatiesCache = Mock.Of();
            string key = GetKey();

            var authenticator = new DeviceScopeTokenAuthenticator(deviceScopeIdensatiesCache, iothubHostName, edgehubHostName, underlyingAuthenticator, true, true);

            var idensaty = Mock.Of(d => d.DeviceId == deviceId && d.Id == deviceId);
            string token = GetDeviceToken(edgehubHostName, deviceId, key);
            SharedAccessSignature sharedAccessSignature = SharedAccessSignature.Parse(edgehubHostName, token);
            string audience = sharedAccessSignature.Audience;

            // Act
            bool isAuthenticated = authenticator.ValidateAudience(audience, idensaty);

            // astert
            astert.True(isAuthenticated);
            Mock.Get(underlyingAuthenticator).VerifyAll();
        }

        [Fact]
        public void InvalidAudienceTest_DeviceId()
        {
            // Arrange
            string iothubHostName = "testiothub.azure-devices.net";
            string edgehubHostName = "edgehub1";
            string deviceId = "d1";

            var underlyingAuthenticator = Mock.Of();
            var deviceScopeIdensatiesCache = Mock.Of();
            string key = GetKey();

            var authenticator = new DeviceScopeTokenAuthenticator(deviceScopeIdensatiesCache, iothubHostName, edgehubHostName, underlyingAuthenticator, true, true);

            var idensaty = Mock.Of(d => d.DeviceId == deviceId && d.Id == deviceId);
            string token = GetDeviceToken(edgehubHostName, "d2", key);
            SharedAccessSignature sharedAccessSignature = SharedAccessSignature.Parse(edgehubHostName, token);
            string audience = sharedAccessSignature.Audience;

            // Act
            bool isAuthenticated = authenticator.ValidateAudience(audience, idensaty);

            // astert
            astert.False(isAuthenticated);
            Mock.Get(underlyingAuthenticator).VerifyAll();
        }

        [Fact]
        public void InvalidAudienceTest_Hostname()
        {
            // Arrange
            string iothubHostName = "testiothub.azure-devices.net";
            string edgehubHostName = "edgehub1";
            string deviceId = "d1";

            var underlyingAuthenticator = Mock.Of();
            var deviceScopeIdensatiesCache = Mock.Of();
            string key = GetKey();

            var authenticator = new DeviceScopeTokenAuthenticator(deviceScopeIdensatiesCache, iothubHostName, edgehubHostName, underlyingAuthenticator, true, true);

            var idensaty = Mock.Of(d => d.DeviceId == deviceId && d.Id == deviceId);
            string token = GetDeviceToken("edgehub2", deviceId, key);
            SharedAccessSignature sharedAccessSignature = SharedAccessSignature.Parse(edgehubHostName, token);
            string audience = sharedAccessSignature.Audience;

            // Act
            bool isAuthenticated = authenticator.ValidateAudience(audience, idensaty);

            // astert
            astert.False(isAuthenticated);
            Mock.Get(underlyingAuthenticator).VerifyAll();
        }

        [Fact]
        public void InvalidAudienceTest_Device_Format()
        {
            // Arrange
            string iothubHostName = "testiothub.azure-devices.net";
            string edgehubHostName = "edgehub1";
            string deviceId = "d1";

            var underlyingAuthenticator = Mock.Of();
            var deviceScopeIdensatiesCache = Mock.Of();

            var authenticator = new DeviceScopeTokenAuthenticator(deviceScopeIdensatiesCache, iothubHostName, edgehubHostName, underlyingAuthenticator, true, true);

            var idensaty = Mock.Of(d => d.DeviceId == deviceId && d.Id == deviceId);
            string audience = $"{iothubHostName}/devices/{deviceId}/foo";

            // Act
            bool isAuthenticated = authenticator.ValidateAudience(audience, idensaty);

            // astert
            astert.False(isAuthenticated);
            Mock.Get(underlyingAuthenticator).VerifyAll();
        }

        [Fact]
        public void InvalidAudienceTest_Module_Format()
        {
            // Arrange
            string iothubHostName = "testiothub.azure-devices.net";
            string edgehubHostName = "edgehub1";
            string deviceId = "d1";
            string moduleId = "m1";

            var underlyingAuthenticator = Mock.Of();
            var deviceScopeIdensatiesCache = Mock.Of();

            var authenticator = new DeviceScopeTokenAuthenticator(deviceScopeIdensatiesCache, iothubHostName, edgehubHostName, underlyingAuthenticator, true, true);

            var idensaty = Mock.Of(d => d.DeviceId == deviceId && d.ModuleId == moduleId && d.Id == $"{deviceId}/{moduleId}");
            string audience = $"{iothubHostName}/devices/{deviceId}/modules/{moduleId}/m1";

            // Act
            bool isAuthenticated = authenticator.ValidateAudience(audience, idensaty);

            // astert
            astert.False(isAuthenticated);
            Mock.Get(underlyingAuthenticator).VerifyAll();
        }

        [Fact]
        public async Task InvalidAuthChainTest_UnauthorizedActor()
        {
            // Arrange
            string iothubHostName = "testiothub.azure-devices.net";
            string edgehubHostName = "edgehub1";
            string rootEdgeId = "rootEdge";
            string actorEdgeId = "childEdge";
            string leafDeviceId = "leaf";
            var authChain = Option.Some(leafDeviceId + ";" + "NotActorEdge" + ";" + rootEdgeId);
            var underlyingAuthenticator = Mock.Of();
            var deviceScopeIdensatiesCache = new Mock();
            string key = GetKey();

            deviceScopeIdensatiesCache.Setup(d => d.GetAuthChain(It.Is(i => i == leafDeviceId)))
                .ReturnsAsync(authChain);

            var authenticator = new DeviceScopeTokenAuthenticator(deviceScopeIdensatiesCache.Object, iothubHostName, edgehubHostName, underlyingAuthenticator, true, true);

            var idensaty = Mock.Of(d => d.DeviceId == actorEdgeId && d.ModuleId == Constants.EdgeHubModuleId && d.Id == $"{actorEdgeId}/{Constants.EdgeHubModuleId}");
            string token = GetDeviceToken(iothubHostName, actorEdgeId, Constants.EdgeHubModuleId, key);
            var tokenCredentials = Mock.Of(t => t.Idensaty == idensaty && t.Token == token);

            // Act
            bool isAuthenticated = await authenticator.AuthenticateAsync(tokenCredentials);

            // astert
            astert.False(isAuthenticated);
        }

        [Fact]
        public async Task ValidateUnderlyingAuthenticatorErrorTest()
        {
            // Arrange
            string iothubHostName = "testiothub.azure-devices.net";
            string edgehubHostName = "edgehub1";
            string deviceId = "d1";
            var underlyingAuthenticator = Mock.Of();
            Mock.Get(underlyingAuthenticator).Setup(u => u.AuthenticateAsync(It.IsAny())).ThrowsAsync(new TimeoutException());
            var deviceScopeIdensatiesCache = new Mock();
            deviceScopeIdensatiesCache.Setup(d => d.GetServiceIdensaty(It.Is(i => i == deviceId)))
                .ReturnsAsync(Option.None());

            IAuthenticator authenticator = new DeviceScopeTokenAuthenticator(deviceScopeIdensatiesCache.Object, iothubHostName, edgehubHostName, underlyingAuthenticator, true, true);

            var idensaty = Mock.Of(d => d.DeviceId == deviceId && d.Id == deviceId);
            string token = GetDeviceToken(iothubHostName, deviceId, GetKey());
            var tokenCredentials = Mock.Of(t => t.Idensaty == idensaty && t.Token == token);

            // Act
            await astert.ThrowsAsync(() => authenticator.AuthenticateAsync(tokenCredentials));

            // astert
            Mock.Get(underlyingAuthenticator).VerifyAll();
            Mock.Get(underlyingAuthenticator).Verify(u => u.AuthenticateAsync(It.IsAny()), Times.Once);
        }

        static string GetDeviceToken(string iothubHostName, string deviceId, string key, TimeSpan timeToLive)
        {
            DateTime startTime = DateTime.UtcNow;
            string audience = WebUtility.UrlEncode($"{iothubHostName}/devices/{deviceId}");
            string expiresOn = SasTokenHelper.BuildExpiresOn(startTime, timeToLive);
            string data = string.Join("\n", new List { audience, expiresOn });
            string signature = Sign(data, key);
            return SasTokenHelper.BuildSasToken(audience, signature, expiresOn);
        }

        static string GetDeviceToken(string iothubHostName, string deviceId, string key)
            => GetDeviceToken(iothubHostName, deviceId, key, TimeSpan.FromHours(1));

        static string GetDeviceToken(string iothubHostName, string deviceId, string moduleId, string key)
        {
            DateTime startTime = DateTime.UtcNow;
            string audience = WebUtility.UrlEncode($"{iothubHostName}/devices/{deviceId}/modules/{moduleId}");
            string expiresOn = SasTokenHelper.BuildExpiresOn(startTime, TimeSpan.FromHours(1));
            string data = string.Join("\n", new List { audience, expiresOn });
            string signature = Sign(data, key);
            return SasTokenHelper.BuildSasToken(audience, signature, expiresOn);
        }

        static string Sign(string requestString, string key)
        {
            using (var algorithm = new HMACSHA256(Convert.FromBase64String(key)))
            {
                return Convert.ToBase64String(algorithm.ComputeHash(Encoding.UTF8.GetBytes(requestString)));
            }
        }

        static string GetKey() => Convert.ToBase64String(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString()));
    }
}