Skip to content

Commit

Permalink
feat(service): Add AAD auth support to Azure US Government cloud. (#1967
Browse files Browse the repository at this point in the history
)
  • Loading branch information
vinagesh authored Jun 8, 2021
1 parent d8f908e commit 8b3056c
Show file tree
Hide file tree
Showing 12 changed files with 223 additions and 27 deletions.
2 changes: 0 additions & 2 deletions common/src/service/CommonConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,6 @@ internal static class CommonConstants

public const string HttpErrorCodeName = "iothub-errorcode";

public static readonly string[] IotHubAadTokenScopes = new string[] { "https://iothubs.azure.net/.default" };

//Service Analytics related
public static class ServiceAnalytics
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Azure.Core;
Expand All @@ -18,12 +19,14 @@ namespace Microsoft.Azure.Devices.DigitalTwin.Authentication
internal class DigitalTwinTokenCredential : DigitalTwinServiceClientCredentials
{
private readonly object _tokenLock = new object();
private readonly string[] _tokenCredentialAuthenticationScopes;
private AccessToken? _cachedAccessToken;
private TokenCredential _credential;

public DigitalTwinTokenCredential(TokenCredential credential)
public DigitalTwinTokenCredential(TokenCredential credential, IReadOnlyList<string> tokenCredentialAuthenticationScopes)
{
_credential = credential;
_tokenCredentialAuthenticationScopes = tokenCredentialAuthenticationScopes.ToArray();
}

public override string GetAuthorizationHeader()
Expand All @@ -35,7 +38,7 @@ public override string GetAuthorizationHeader()
|| TokenHelper.IsCloseToExpiry(_cachedAccessToken.Value.ExpiresOn))
{
_cachedAccessToken = _credential.GetToken(
new TokenRequestContext(CommonConstants.IotHubAadTokenScopes),
new TokenRequestContext(_tokenCredentialAuthenticationScopes),
new CancellationToken());
}
}
Expand Down
43 changes: 38 additions & 5 deletions iothub/service/src/DigitalTwin/DigitalTwinClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,23 +105,56 @@ public static DigitalTwinClient CreateFromConnectionString(string connectionStri
/// <returns>An instance of <see cref="DigitalTwinClient"/>.</returns>
/// <remarks>
/// For more information on configuring IoT hub with Azure Active Directory, see <see href="https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-dev-guide-azure-ad-rbac"/>
/// This constructor internally uses <see cref="IotHubAuthenticationScopes.DefaultAuthenticationScopes"/>, which is used for
/// any public or private cloud other than Azure US Government cloud.
/// For Azure US Government cloud users, use the constructor with <see cref="DigitalTwinClientOptions"/> and set the
/// <see cref="DigitalTwinClientOptions.TokenCredentialAuthenticationScopes"/> to <see cref="IotHubAuthenticationScopes.AzureGovernmentAuthenticationScopes"/>.
/// </remarks>
public static DigitalTwinClient Create(
string hostName,
TokenCredential credential,
params DelegatingHandler[] handlers)
{
return Create(hostName, credential, null, handlers);
}

/// <summary>
/// Creates an instance of <see cref="DigitalTwinClient"/>.
/// </summary>
/// <param name="hostName">IoT hub host name.</param>
/// <param name="credential">Azure Active Directory credentials to authenticate with IoT hub. See <see cref="TokenCredential"/></param>
/// <param name="options">Options that allow configuration of the DigitalTwinClient instance during initialization.</param>
/// <param name="handlers">The delegating handlers to add to the http client pipeline. You can add handlers for tracing, implementing a retry strategy, routing requests through a proxy, etc.</param>
/// <returns>An instance of <see cref="DigitalTwinClient"/>.</returns>
/// <remarks>
/// For more information on configuring IoT hub with Azure Active Directory, see <see href="https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-dev-guide-azure-ad-rbac"/>
/// This constructor sets the default for <see cref="DigitalTwinClientOptions.TokenCredentialAuthenticationScopes"/> to
/// <see cref="IotHubAuthenticationScopes.DefaultAuthenticationScopes"/>, which is used for any public or private cloud other than Azure US Government cloud.
/// For Azure US Government cloud users, set the <see cref="DigitalTwinClientOptions.TokenCredentialAuthenticationScopes"/>
/// to <see cref="IotHubAuthenticationScopes.AzureGovernmentAuthenticationScopes"/>.
/// </remarks>
public static DigitalTwinClient Create(
string hostName,
TokenCredential credential,
DigitalTwinClientOptions options,
params DelegatingHandler[] handlers)
{
if (string.IsNullOrEmpty(hostName))
{
throw new ArgumentNullException($"{nameof(hostName)}, Parameter cannot be null or empty");
throw new ArgumentNullException(nameof(hostName), "Parameter cannot be null or empty.");
}

if (credential == null)
{
throw new ArgumentNullException($"{nameof(credential)}, Parameter cannot be null");
throw new ArgumentNullException(nameof(credential));
}

if (options == null)
{
options = new DigitalTwinClientOptions();
}

var tokenCredential = new DigitalTwinTokenCredential(credential);
var tokenCredential = new DigitalTwinTokenCredential(credential, options.TokenCredentialAuthenticationScopes);
return new DigitalTwinClient(hostName, tokenCredential, handlers);
}

Expand All @@ -139,12 +172,12 @@ public static DigitalTwinClient Create(
{
if (string.IsNullOrEmpty(hostName))
{
throw new ArgumentNullException($"{nameof(hostName)}, Parameter cannot be null or empty");
throw new ArgumentNullException(nameof(hostName), "Parameter cannot be null or empty.");
}

if (credential == null)
{
throw new ArgumentNullException($"{nameof(credential)}, Parameter cannot be null");
throw new ArgumentNullException(nameof(credential));
}

var sasCredential = new DigitalTwinSasCredential(credential);
Expand Down
26 changes: 26 additions & 0 deletions iothub/service/src/DigitalTwin/DigitalTwinClientOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Collections.Generic;
using System.Text;

namespace Microsoft.Azure.Devices
{
/// <summary>
/// Options that allow configuration of the DigitalTwinClient instance during initialization.
/// </summary>
public class DigitalTwinClientOptions
{
/// <summary>
/// The authentication scopes to use when requesting access tokens from Azure Active Directory for authenticating with the
/// IoT Hub.
/// </summary>
/// <remarks>
/// This value defaults <see cref="IotHubAuthenticationScopes.DefaultAuthenticationScopes"/>, which is used for
/// any public or private cloud other than Azure US Government cloud.
/// For Azure US Government cloud users, this value must be set to <see cref="IotHubAuthenticationScopes.AzureGovernmentAuthenticationScopes"/>.
/// </remarks>
public IReadOnlyList<string> TokenCredentialAuthenticationScopes { get; set; } = IotHubAuthenticationScopes.DefaultAuthenticationScopes;
}
}
26 changes: 26 additions & 0 deletions iothub/service/src/IotHubAuthenticationScopes.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Collections.Generic;
using System.Text;

namespace Microsoft.Azure.Devices
{
/// <summary>
/// This class contains the most commonly used Azure Active Directory token audience scopes. They should be used as
/// inputs when setting the authentication scopes in the client options classes.
/// </summary>
public static class IotHubAuthenticationScopes
{
/// <summary>
/// The default value for all client options. This value should be used for any public or private cloud other than Azure US Government cloud.
/// </summary>
public static readonly IReadOnlyList<string> DefaultAuthenticationScopes = new List<string> { "https://iothubs.azure.net/.default" };

/// <summary>
/// This value should be used for Azure US Government cloud.
/// </summary>
public static readonly IReadOnlyList<string> AzureGovernmentAuthenticationScopes = new List<string> { "https://iothubs.azure.us/.default" };
}
}
9 changes: 6 additions & 3 deletions iothub/service/src/IotHubTokenCredentialProperties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Microsoft.Azure.Amqp;
using System.Threading;
using Microsoft.Azure.Devices.Common;
using System.Linq;

#if !NET451

Expand All @@ -24,6 +25,7 @@ internal class IotHubTokenCrendentialProperties
{
#if !NET451
private const string _tokenType = "Bearer";
private readonly string[] _tokenCredentialAuthenticationScopes;
private readonly TokenCredential _credential;
private readonly object _tokenLock = new object();
private AccessToken? _cachedAccessToken;
Expand All @@ -37,9 +39,10 @@ public IotHubTokenCrendentialProperties()
}
#else

public IotHubTokenCrendentialProperties(string hostName, TokenCredential credential) : base(hostName)
public IotHubTokenCrendentialProperties(string hostName, TokenCredential credential, IReadOnlyList<string> tokenCredentialAuthenticationScopes) : base(hostName)
{
_credential = credential;
_tokenCredentialAuthenticationScopes = tokenCredentialAuthenticationScopes.ToArray();
}

#endif
Expand All @@ -58,7 +61,7 @@ public override string GetAuthorizationHeader()
|| TokenHelper.IsCloseToExpiry(_cachedAccessToken.Value.ExpiresOn))
{
_cachedAccessToken = _credential.GetToken(
new TokenRequestContext(CommonConstants.IotHubAadTokenScopes),
new TokenRequestContext(_tokenCredentialAuthenticationScopes),
new CancellationToken());
}
}
Expand All @@ -79,7 +82,7 @@ public async override Task<CbsToken> GetTokenAsync(Uri namespaceAddress, string

#else
AccessToken token = await _credential.GetTokenAsync(
new TokenRequestContext(CommonConstants.IotHubAadTokenScopes),
new TokenRequestContext(_tokenCredentialAuthenticationScopes),
new CancellationToken()).ConfigureAwait(false);
return new CbsToken(
$"{_tokenType} {token.Token}",
Expand Down
23 changes: 19 additions & 4 deletions iothub/service/src/JobClient/JobClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,26 +56,41 @@ public static JobClient CreateFromConnectionString(string connectionString, Http
/// <param name="hostName">IoT hub host name.</param>
/// <param name="credential">Azure Active Directory credentials to authenticate with IoT hub. See <see cref="TokenCredential"/></param>
/// <param name="transportSettings">The HTTP transport settings.</param>
/// <param name="options">Options that allow configuration of the JobClient instance during initialization.</param>
/// <returns>An instance of <see cref="JobClient"/>.</returns>
/// <remarks>
/// For more information on configuring IoT hub with Azure Active Directory, see <see href="https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-dev-guide-azure-ad-rbac"/>
/// This constructor sets the default for <see cref="JobClientOptions.TokenCredentialAuthenticationScopes"/> to
/// <see cref="IotHubAuthenticationScopes.DefaultAuthenticationScopes"/>, which is used for any public or private cloud other than Azure US Government cloud.
/// For Azure US Government cloud users, set the <see cref="JobClientOptions.TokenCredentialAuthenticationScopes"/>
/// to <see cref="IotHubAuthenticationScopes.AzureGovernmentAuthenticationScopes"/>.
/// </remarks>
public static JobClient Create(
string hostName,
TokenCredential credential,
HttpTransportSettings transportSettings = default)
HttpTransportSettings transportSettings = default,
JobClientOptions options = default)
{
if (string.IsNullOrEmpty(hostName))
{
throw new ArgumentNullException(nameof(hostName));
throw new ArgumentNullException(nameof(hostName), "Parameter cannot be null or empty.");
}

if (credential == null)
{
throw new ArgumentNullException(nameof(credential));
}

var tokenCredentialProperties = new IotHubTokenCrendentialProperties(hostName, credential);
if (options == null)
{
options = new JobClientOptions();
}

var tokenCredentialProperties = new IotHubTokenCrendentialProperties(
hostName,
credential,
options.TokenCredentialAuthenticationScopes);

return new HttpJobClient(tokenCredentialProperties, transportSettings ?? new HttpTransportSettings());
}

Expand All @@ -93,7 +108,7 @@ public static JobClient Create(
{
if (string.IsNullOrEmpty(hostName))
{
throw new ArgumentNullException(nameof(hostName));
throw new ArgumentNullException(nameof(hostName), "Parameter cannot be null or empty.");
}

if (credential == null)
Expand Down
26 changes: 26 additions & 0 deletions iothub/service/src/JobClient/JobClientOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Collections.Generic;
using System.Text;

namespace Microsoft.Azure.Devices
{
/// <summary>
/// Options that allow configuration of the JobClient instance during initialization.
/// </summary>
public class JobClientOptions
{
/// <summary>
/// The authentication scopes to use when requesting access tokens from Azure Active Directory for authenticating with the
/// IoT Hub.
/// </summary>
/// <remarks>
/// This value defaults <see cref="IotHubAuthenticationScopes.DefaultAuthenticationScopes"/>, which is used for
/// any public or private cloud other than Azure US Government cloud.
/// For Azure US Government cloud users, this value must be set to <see cref="IotHubAuthenticationScopes.AzureGovernmentAuthenticationScopes"/>.
/// </remarks>
public IReadOnlyList<string> TokenCredentialAuthenticationScopes { get; set; } = IotHubAuthenticationScopes.DefaultAuthenticationScopes;
}
}
27 changes: 21 additions & 6 deletions iothub/service/src/RegistryManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,26 +62,41 @@ public static RegistryManager CreateFromConnectionString(string connectionString
/// <param name="hostName">IoT hub host name.</param>
/// <param name="credential">Azure Active Directory credentials to authenticate with IoT hub. See <see cref="TokenCredential"/></param>
/// <param name="transportSettings">The HTTP transport settings.</param>
/// <param name="options">Options that allow configuration of the RegistryManager instance during initialization.</param>
/// <returns>An instance of <see cref="RegistryManager"/>.</returns>
/// <remarks>
/// For more information on configuring IoT hub with Azure Active Directory, see <see href="https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-dev-guide-azure-ad-rbac"/>
/// This constructor sets the default for <see cref="RegistryManagerOptions.TokenCredentialAuthenticationScopes"/> to
/// <see cref="IotHubAuthenticationScopes.DefaultAuthenticationScopes"/>, which is used for any public or private cloud other than Azure US Government cloud.
/// For Azure US Government cloud users, set the <see cref="RegistryManagerOptions.TokenCredentialAuthenticationScopes"/>
/// to <see cref="IotHubAuthenticationScopes.AzureGovernmentAuthenticationScopes"/>.
/// </remarks>
public static RegistryManager Create(
string hostName,
TokenCredential credential,
HttpTransportSettings transportSettings = default)
HttpTransportSettings transportSettings = default,
RegistryManagerOptions options = default)
{
if (string.IsNullOrEmpty(hostName))
{
throw new ArgumentNullException($"{nameof(hostName)}, Parameter cannot be null or empty");
throw new ArgumentNullException(nameof(hostName), "Parameter cannot be null or empty.");
}

if (credential == null)
{
throw new ArgumentNullException($"{nameof(credential)}, Parameter cannot be null");
throw new ArgumentNullException(nameof(credential));
}

var tokenCredentialProperties = new IotHubTokenCrendentialProperties(hostName, credential);
if (options == null)
{
options = new RegistryManagerOptions();
}

var tokenCredentialProperties = new IotHubTokenCrendentialProperties(
hostName,
credential,
options.TokenCredentialAuthenticationScopes);

return new HttpRegistryManager(tokenCredentialProperties, transportSettings ?? new HttpTransportSettings());
}

Expand All @@ -99,12 +114,12 @@ public static RegistryManager Create(
{
if (string.IsNullOrEmpty(hostName))
{
throw new ArgumentNullException($"{nameof(hostName)}, Parameter cannot be null or empty");
throw new ArgumentNullException(nameof(hostName), "Parameter cannot be null or empty.");
}

if (credential == null)
{
throw new ArgumentNullException($"{nameof(credential)}, Parameter cannot be null");
throw new ArgumentNullException(nameof(credential));
}

var sasCredentialProperties = new IotHubSasCredentialProperties(hostName, credential);
Expand Down
26 changes: 26 additions & 0 deletions iothub/service/src/RegistryManagerOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Collections.Generic;
using System.Text;

namespace Microsoft.Azure.Devices
{
/// <summary>
/// Options that allow configuration of the RegistryManager instance during initialization.
/// </summary>
public class RegistryManagerOptions
{
/// <summary>
/// The authentication scopes to use when requesting access tokens from Azure Active Directory for authenticating with the
/// IoT Hub.
/// </summary>
/// <remarks>
/// This value defaults <see cref="IotHubAuthenticationScopes.DefaultAuthenticationScopes"/>, which is used for
/// any public or private cloud other than Azure US Government cloud.
/// For Azure US Government cloud users, this value must be set to <see cref="IotHubAuthenticationScopes.AzureGovernmentAuthenticationScopes"/>.
/// </remarks>
public IReadOnlyList<string> TokenCredentialAuthenticationScopes { get; set; } = IotHubAuthenticationScopes.DefaultAuthenticationScopes;
}
}
Loading

0 comments on commit 8b3056c

Please sign in to comment.