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()); + } + } + } + } +}