diff --git a/sdk/identity/Azure.Identity/assets.json b/sdk/identity/Azure.Identity/assets.json
index a93ca15ca276f..2405b9dd70f26 100644
--- a/sdk/identity/Azure.Identity/assets.json
+++ b/sdk/identity/Azure.Identity/assets.json
@@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "net",
"TagPrefix": "net/identity/Azure.Identity",
- "Tag": "net/identity/Azure.Identity_f0e02fe424"
+ "Tag": "net/identity/Azure.Identity_0224d294dd"
}
diff --git a/sdk/identity/Azure.Identity/tests/Azure.Identity.Tests.csproj b/sdk/identity/Azure.Identity/tests/Azure.Identity.Tests.csproj
index d1d4cfc596ed7..82fee3484292b 100644
--- a/sdk/identity/Azure.Identity/tests/Azure.Identity.Tests.csproj
+++ b/sdk/identity/Azure.Identity/tests/Azure.Identity.Tests.csproj
@@ -22,5 +22,6 @@
+
diff --git a/sdk/identity/Azure.Identity/tests/IdentityTestEnvironment.cs b/sdk/identity/Azure.Identity/tests/IdentityTestEnvironment.cs
index 0fc6f32ed41f2..e76f387ba9ff0 100644
--- a/sdk/identity/Azure.Identity/tests/IdentityTestEnvironment.cs
+++ b/sdk/identity/Azure.Identity/tests/IdentityTestEnvironment.cs
@@ -10,6 +10,11 @@ namespace Azure.Identity.Tests
public class IdentityTestEnvironment : TestEnvironment
{
public string IdentityTenantId => GetRecordedVariable("AZURE_IDENTITY_TEST_TENANTID");
+ public string MultiTenantAppTenantId => GetRecordedVariable("AZURE_IDENTITY_MULTI_TENANT_TENANT_ID");
+ public string MultiTenantAppClientId => GetRecordedVariable("AZURE_IDENTITY_MULTI_TENANT_CLIENT_ID");
+ public string MultiTenantAppClientSecret => GetRecordedVariable("AZURE_IDENTITY_MULTI_TENANT_CLIENT_SECRET", options => options.IsSecret());
+ public string MultiTenantUserName => GetRecordedVariable("AZURE_IDENTITY_MULTI_TENANT_USERNAME");
+ public string MultiTenantPassword => GetRecordedVariable("AZURE_IDENTITY_MULTI_TENANT_PASSWORD", options => options.IsSecret());
public string Username => GetRecordedVariable("AZURE_IDENTITY_TEST_USERNAME");
public string Password => GetVariable("AZURE_IDENTITY_TEST_PASSWORD");
public string IdentityClientId => GetVariable("AZURE_IDENTITY_TEST_CLIENT_ID");
diff --git a/sdk/identity/Azure.Identity/tests/MultiTenantLiveTests.cs b/sdk/identity/Azure.Identity/tests/MultiTenantLiveTests.cs
new file mode 100644
index 0000000000000..28b80956d2f85
--- /dev/null
+++ b/sdk/identity/Azure.Identity/tests/MultiTenantLiveTests.cs
@@ -0,0 +1,131 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Threading.Tasks;
+using Azure.Core;
+using Azure.Core.TestFramework;
+using Azure.Core.Pipeline;
+using NUnit.Framework;
+using System.Net;
+
+namespace Azure.Identity.Tests
+{
+ public class MultiTenantLiveTests : IdentityRecordedTestBase
+ {
+ public MultiTenantLiveTests(bool isAsync) : base(isAsync)
+ { }
+
+ private IdentityTestClient _client;
+
+ [RecordedTest]
+ public async Task CallGraphWithClientSecret()
+ {
+ var tenantId = TestEnvironment.MultiTenantAppTenantId;
+ var clientId = TestEnvironment.MultiTenantAppClientId;
+ var secret = TestEnvironment.MultiTenantAppClientSecret;
+
+ var options = InstrumentClientOptions(new TokenCredentialOptions());
+ var credential = InstrumentClient(new ClientSecretCredential(tenantId, clientId, secret, options));
+ _client = InstrumentClient(new IdentityTestClient(
+ credential,
+ new Uri("https://graph.microsoft.com/v1.0/applications/$count"),
+ options));
+
+ var response = await _client.CallGraphAsync("https://graph.microsoft.com/.default");
+
+ Assert.AreEqual((int)HttpStatusCode.OK, response.GetRawResponse().Status);
+ Assert.Greater(response.Value, 0);
+ }
+
+ [RecordedTest]
+ public async Task GraphWithUsernamePassword()
+ {
+ var tenantId = TestEnvironment.MultiTenantAppTenantId;
+ var clientId = TestEnvironment.MultiTenantAppClientId;
+ var username = TestEnvironment.MultiTenantUserName;
+ var password = TestEnvironment.MultiTenantPassword;
+
+ var options = InstrumentClientOptions(new TokenCredentialOptions());
+ var credential = InstrumentClient(new UsernamePasswordCredential(username, password, tenantId, clientId, options));
+
+ _client = InstrumentClient(new IdentityTestClient(
+ credential,
+ new Uri("https://graph.microsoft.com/v1.0/applications/$count"),
+ options));
+
+ var response = await _client.CallGraphAsync("User.Read");
+
+ Assert.AreEqual((int)HttpStatusCode.OK, response.GetRawResponse().Status);
+ Assert.Greater(response.Value, 0);
+ }
+
+ public class IdentityTestClient
+ {
+ public IdentityTestClient(TokenCredential credential, Uri uri, TokenCredentialOptions options)
+ {
+ this.credential = credential;
+ Uri = uri;
+ _pipeline = HttpPipelineBuilder.Build(options);
+ }
+
+ protected IdentityTestClient() { }
+
+ private TokenCredential credential { get; }
+ private Uri Uri { get; }
+ private HttpPipeline _pipeline { get; }
+
+ [ForwardsClientCalls(true)]
+ public virtual Response CallGraph(string scope)
+ {
+ var tokenRequestContext = new TokenRequestContext(new[] { scope });
+ AccessToken token = credential.GetTokenAsync(tokenRequestContext, default).GetAwaiter().GetResult();
+ Request request = _pipeline.CreateRequest();
+ request.Method = RequestMethod.Get;
+ request.Uri.Reset(new Uri("https://graph.microsoft.com/v1.0/applications/$count"));
+ request.Headers.Add("Authorization", $"Bearer {token.Token}");
+ request.Headers.Add("ConsistencyLevel", "eventual");
+
+ Response response = _pipeline.SendRequest(request, default);
+ if (response.IsError)
+ {
+ throw new Exception(response.ReasonPhrase);
+ }
+ if (int.TryParse(response.Content.ToString(), out int result))
+ {
+ return Response.FromValue(result, response);
+ }
+ else
+ {
+ throw new Exception("Could not parse response:\n" + response.Content.ToString());
+ }
+ }
+
+ [ForwardsClientCalls(true)]
+ public virtual async Task> CallGraphAsync(string scope)
+ {
+ var tokenRequestContext = new TokenRequestContext(new[] { scope });
+ AccessToken token = await credential.GetTokenAsync(tokenRequestContext, default);
+ Request request = _pipeline.CreateRequest();
+ request.Method = RequestMethod.Get;
+ request.Uri.Reset(new Uri("https://graph.microsoft.com/v1.0/applications/$count"));
+ request.Headers.Add("Authorization", $"Bearer {token.Token}");
+ request.Headers.Add("ConsistencyLevel", "eventual");
+
+ Response response = await _pipeline.SendRequestAsync(request, default);
+ if (response.IsError)
+ {
+ throw new Exception(response.ReasonPhrase);
+ }
+ if (int.TryParse(response.Content.ToString(), out int result))
+ {
+ return Response.FromValue(result, response);
+ }
+ else
+ {
+ throw new Exception("Could not parse response:\n" + response.Content.ToString());
+ }
+ }
+ }
+ }
+}