Skip to content

Commit 333f687

Browse files
committed
More updates
1 parent 27fd311 commit 333f687

File tree

7 files changed

+114
-92
lines changed

7 files changed

+114
-92
lines changed

Directory.Packages.props

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,10 @@
3939
</ItemGroup>
4040
<ItemGroup>
4141
<!-- Packages for the tests -->
42-
<PackageVersion Include="Azure.Security.KeyVault.Certificates" Version="4.6.0" />
43-
<PackageVersion Include="Azure.Security.KeyVault.Secrets" Version="4.6.0" />
42+
<PackageVersion Include="Azure.Security.KeyVault.Certificates" Version="4.7.0" />
43+
<PackageVersion Include="Azure.Security.KeyVault.Secrets" Version="4.7.0" />
44+
<PackageVersion Include="Azure.Identity" Version="1.13.1" />
45+
<PackageVersion Include="Azure.Identity.Broker" Version="1.2.0" />
4446
<PackageVersion Include="BenchmarkDotNet" Version="0.13.6" />
4547
<PackageVersion Include="BenchmarkDotNet.Diagnostics.Windows" Version="0.13.6" />
4648
<PackageVersion Include="coverlet.collector" Version="3.1.2" />

tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/CiamIntegrationTests.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -199,11 +199,5 @@ public async Task OBOCiam_CustomDomain_ReturnsValidTokens()
199199
Assert.AreEqual(atHash, userCacheRecorder.LastAfterAccessNotificationArgs.SuggestedCacheKey);
200200
Assert.AreEqual(TokenSource.Cache, resultObo.AuthenticationResultMetadata.TokenSource);
201201
}
202-
203-
private string GetCiamSecret()
204-
{
205-
KeyVaultSecretsProvider provider = new KeyVaultSecretsProvider();
206-
return provider.GetSecretByName("msidlabciam2-cc").Value;
207-
}
208202
}
209203
}

tests/Microsoft.Identity.Test.Integration.netcore/Infrastructure/PoPValidator.cs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,7 @@ namespace Microsoft.Identity.Test.Integration.Infrastructure
1717
public class PoPValidator
1818
{
1919
// This endpoint is hosted in the MSID Lab and is able to verify any pop token bound to an HTTP request
20-
private const string PoPValidatorEndpoint = "https://signedhttprequest.azurewebsites.net/api/validateSHR";
2120
private static HttpClient s_httpClient = new HttpClient();
22-
private static Lazy<string> s_popValidationEndpointLazy = new Lazy<string>(
23-
() => LabUserHelper.KeyVaultSecretsProviderMsal.GetSecretByName(
24-
"automation-pop-validation-endpoint",
25-
"841fc7c2ccdd48d7a9ef727e4ae84325").Value);
2621

2722
/// <summary>
2823
/// This calls a special endpoint that validates any POP token against a configurable HTTP request.

tests/Microsoft.Identity.Test.LabInfrastructure/KeyVaultSecretsProvider.cs

Lines changed: 6 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public class KeyVaultInstance
2424
public const string MsalTeam = "https://buildautomation.vault.azure.net/";
2525
}
2626

27-
public class KeyVaultSecretsProvider : IDisposable
27+
public class KeyVaultSecretsProvider
2828
{
2929
private CertificateClient _certificateClient;
3030
private SecretClient _secretClient;
@@ -58,41 +58,20 @@ public class KeyVaultSecretsProvider : IDisposable
5858
/// </remarks>
5959
public KeyVaultSecretsProvider(string keyVaultAddress = KeyVaultInstance.MSIDLab)
6060
{
61-
var credentials = GetKeyVaultCredentialAsync().GetAwaiter().GetResult();
61+
var credential = LabAuthenticationHelper.GetTokenCredential();
6262
var keyVaultAddressUri = new Uri(keyVaultAddress);
63-
_certificateClient = new CertificateClient(keyVaultAddressUri, credentials);
64-
_secretClient = new SecretClient(keyVaultAddressUri, credentials);
65-
}
66-
67-
~KeyVaultSecretsProvider()
68-
{
69-
Dispose();
63+
_certificateClient = new CertificateClient(keyVaultAddressUri, credential);
64+
_secretClient = new SecretClient(keyVaultAddressUri, credential);
7065
}
7166

7267
public KeyVaultSecret GetSecretByName(string secretName)
7368
{
7469
return _secretClient.GetSecret(secretName).Value;
7570
}
76-
77-
public KeyVaultSecret GetSecretByName(string secretName, string secretVersion)
78-
{
79-
return _secretClient.GetSecret(secretName, secretVersion).Value;
80-
}
81-
71+
8272
public async Task<X509Certificate2> GetCertificateWithPrivateMaterialAsync(string certName)
8373
{
8474
return await _certificateClient.DownloadCertificateAsync(certName).ConfigureAwait(false);
85-
}
86-
87-
private async Task<TokenCredential> GetKeyVaultCredentialAsync()
88-
{
89-
var accessToken = await LabAuthenticationHelper.GetKeyVaultAccessToken().ConfigureAwait(false);
90-
return DelegatedTokenCredential.Create((_, __) => accessToken);
91-
}
92-
93-
public void Dispose()
94-
{
95-
GC.SuppressFinalize(this);
96-
}
75+
}
9776
}
9877
}

tests/Microsoft.Identity.Test.LabInfrastructure/LabAuthenticationHelper.cs

Lines changed: 73 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -8,60 +8,103 @@
88
using System.Threading.Tasks;
99
using Azure.Core;
1010
using Microsoft.Identity.Client;
11+
using Microsoft.Identity.Client.Broker;
1112
using Microsoft.Identity.Test.Unit;
1213
using Microsoft.Identity.Test.Common.Core.Mocks;
1314
using Microsoft.Identity.Test.Common.Core.Helpers;
15+
using Azure.Identity;
16+
using System.Runtime.InteropServices;
17+
using System.Linq;
18+
using Azure.Identity.Broker;
19+
using System.Diagnostics;
20+
using System.Collections.Generic;
1421

1522
namespace Microsoft.Identity.Test.LabInfrastructure
1623
{
1724
internal static class LabAuthenticationHelper
1825
{
1926
public static async Task<AccessToken> GetAccessTokenForLabAPIAsync()
2027
{
21-
string[] scopes = [LabApiConstants.LabScope];
28+
var tokenCredential = GetTokenCredential();
2229

23-
return await GetLabAccessTokenAsync(
24-
LabApiConstants.LabClientInstance + LabApiConstants.LabClientTenantId,
25-
scopes).ConfigureAwait(false);
30+
return await tokenCredential
31+
.GetTokenAsync(new TokenRequestContext([LabApiConstants.LabScope]), default)
32+
.ConfigureAwait(false);
2633
}
2734

2835
public static async Task<AccessToken> GetKeyVaultAccessToken()
2936
{
30-
var accessToken = await GetLabAccessTokenAsync(
31-
"https://login.microsoftonline.com/72f988bf-86f1-41af-91ab-2d7cd011db47/",
32-
new[] { "https://vault.azure.net/.default" }).ConfigureAwait(false);
37+
var tokenCredential = GetTokenCredential();
3338

34-
return accessToken;
39+
return await tokenCredential
40+
.GetTokenAsync(new TokenRequestContext(["https://vault.azure.net/.default"]), default)
41+
.ConfigureAwait(false);
3542
}
3643

37-
private static async Task<AccessToken> GetLabAccessTokenAsync(string authority, string[] scopes)
44+
internal static TokenCredential GetTokenCredential()
3845
{
39-
AuthenticationResult authResult;
40-
IConfidentialClientApplication confidentialApp;
41-
X509Certificate2 cert;
42-
43-
cert = CertificateHelper.FindCertificateByName(TestConstants.AutomationTestCertName);
44-
if (cert == null)
46+
TokenCredential tokenCredential;
47+
if (Environment.GetEnvironmentVariable("TF_BUILD") == null)
48+
{
49+
Debug.WriteLine("[LabAPI] Not on CI, using interactive browser/broker credential");
50+
tokenCredential = GetAzureCredentialForDevBox();
51+
}
52+
else
4553
{
46-
throw new InvalidOperationException(
47-
"Test setup error - cannot find a certificate in the My store for KeyVault. This is available for Microsoft employees only.");
54+
Debug.WriteLine("[LabAPI] On CI, using ADO federation");
55+
tokenCredential = GetAzureCredentialForCI();
4856
}
4957

50-
confidentialApp = ConfidentialClientApplicationBuilder
51-
.Create(LabApiConstants.LabClientId)
52-
.WithAuthority(new Uri(authority), true)
53-
.WithCacheOptions(CacheOptions.EnableSharedCacheOptions)
54-
.WithCertificate(cert, true)
55-
.Build();
58+
return tokenCredential;
59+
}
60+
61+
private static TokenCredential GetAzureCredentialForCI()
62+
{
63+
// as per https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/identity/Azure.Identity/samples/OtherCredentialSamples.md#authenticating-in-azure-pipelines-with-service-connections
64+
65+
string clientId = "4b7a4b0b-ecb2-409e-879a-1e21a15ddaf6"; // UAMI client ID
66+
string tenantId = LabApiConstants.LabClientTenantId;
67+
string serviceConnectionId = "6eeeb73d-37aa-4d78-83b7-728101b8bddd";
5668

57-
authResult = await confidentialApp
58-
.AcquireTokenForClient(scopes)
59-
.WithSendX5C(true)
60-
.ExecuteAsync(CancellationToken.None)
61-
.ConfigureAwait(false);
69+
var pipelinesCredential = new AzurePipelinesCredential(
70+
tenantId,
71+
clientId,
72+
serviceConnectionId,
73+
Environment.GetEnvironmentVariable("SYSTEM_ACCESSTOKEN"),
74+
new AzurePipelinesCredentialOptions()
75+
{
76+
TokenCachePersistenceOptions = new TokenCachePersistenceOptions()
77+
{
78+
Name = "MSIDLabTokenCache",
79+
UnsafeAllowUnencryptedStorage = true // We generally use headless Linux, so cannot use LibSecret. This is the ~same level of protection as SSH keys.
80+
}
81+
});
6282

63-
return new AccessToken(authResult.AccessToken, authResult.ExpiresOn);
83+
return pipelinesCredential;
6484
}
65-
}
6685

86+
// TODO: test this on MacOs / Linux WSL
87+
private static TokenCredential GetAzureCredentialForDevBox()
88+
{
89+
InteractiveBrowserCredential interactiveBrowserCredential = new InteractiveBrowserCredential(
90+
new InteractiveBrowserCredentialBrokerOptions(GetForegroundWindow())
91+
{
92+
ClientId = LabApiConstants.LabClientId,
93+
TenantId = LabApiConstants.LabClientTenantId,
94+
TokenCachePersistenceOptions = new TokenCachePersistenceOptions()
95+
{
96+
Name = "MSIDLabTokenCache",
97+
UnsafeAllowUnencryptedStorage = true // We generally use headless Linux, so cannot use LibSecret. This is the ~same level of protection as SSH keys.
98+
},
99+
RedirectUri = new Uri("http://localhost"), // On Mac and Linux, MSAL will fallback to browser
100+
UseDefaultBrokerAccount = true //
101+
102+
});
103+
104+
return interactiveBrowserCredential;
105+
}
106+
107+
[DllImport("user32.dll")]
108+
private static extern IntPtr GetForegroundWindow();
109+
}
67110
}

tests/Microsoft.Identity.Test.LabInfrastructure/LabUserHelper.cs

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
// Copyright (c) Microsoft Corporation. All rights reserved.
2-
// Licensed under the MIT License.
3-
4-
using System;
1+
using System;
52
using System.Collections.Concurrent;
63
using System.Diagnostics;
74
using System.Threading.Tasks;
@@ -11,22 +8,41 @@ namespace Microsoft.Identity.Test.LabInfrastructure
118
{
129
public static class LabUserHelper
1310
{
14-
private static readonly LabServiceApi s_labService;
11+
private static readonly LabServiceApi s_labService = new LabServiceApi();
1512
private static readonly ConcurrentDictionary<UserQuery, LabResponse> s_userCache =
1613
new ConcurrentDictionary<UserQuery, LabResponse>();
1714

18-
public static KeyVaultSecretsProvider KeyVaultSecretsProviderMsal { get; }
19-
public static KeyVaultSecretsProvider KeyVaultSecretsProviderMsid { get; }
15+
public static KeyVaultSecretsProvider KeyVaultSecretsProviderMsal { get; private set; }
16+
public static KeyVaultSecretsProvider KeyVaultSecretsProviderMsid { get; private set; }
17+
18+
private static bool _isInitialized = false;
19+
private static readonly object _initializationLock = new object();
2020

21-
static LabUserHelper()
21+
public static void Initialize()
2222
{
23-
KeyVaultSecretsProviderMsal = new KeyVaultSecretsProvider(KeyVaultInstance.MsalTeam);
24-
KeyVaultSecretsProviderMsid = new KeyVaultSecretsProvider(KeyVaultInstance.MSIDLab);
25-
s_labService = new LabServiceApi();
23+
if (_isInitialized)
24+
{
25+
return;
26+
}
27+
28+
lock (_initializationLock)
29+
{
30+
if (_isInitialized)
31+
{
32+
return;
33+
}
34+
35+
KeyVaultSecretsProviderMsal = new KeyVaultSecretsProvider(KeyVaultInstance.MsalTeam);
36+
KeyVaultSecretsProviderMsid = new KeyVaultSecretsProvider(KeyVaultInstance.MSIDLab);
37+
38+
_isInitialized = true;
39+
}
2640
}
2741

2842
internal static async Task<LabResponse> GetLabUserDataAsync(UserQuery query)
2943
{
44+
Initialize();
45+
3046
if (s_userCache.ContainsKey(query))
3147
{
3248
Trace.WriteLine("Lab user cache hit. Selected user: " + s_userCache[query].User.Upn);
@@ -54,23 +70,16 @@ public static Task<LabResponse> GetLabUserDataForSpecificUserAsync(string upn)
5470

5571
public static async Task<string> GetMSIEnvironmentVariablesAsync(string uri)
5672
{
73+
Initialize();
5774
string result = await s_labService.GetLabResponseAsync(uri).ConfigureAwait(false);
5875
return result;
5976
}
6077

61-
/// <summary>
62-
/// Returns the AAD cloud user idlab1@msidlab4.onmicrosoft.com
63-
/// </summary>
64-
/// <returns></returns>
6578
public static Task<LabResponse> GetDefaultUserAsync()
6679
{
6780
return GetLabUserDataAsync(UserQuery.PublicAadUserQuery);
6881
}
6982

70-
/// <summary>
71-
/// Returns the AAD cloud user idlab@msidlab4.onmicrosoft.com
72-
/// </summary>
73-
/// <returns></returns>
7483
public static Task<LabResponse> GetDefaultUser2Async()
7584
{
7685
return GetLabUserDataAsync(UserQuery.PublicAadUser2Query);
@@ -112,9 +121,6 @@ public static async Task<LabResponse> GetB2CMSAAccountAsync()
112121
return response;
113122
}
114123

115-
/// <summary>
116-
/// Gets a user for which you know the UPN.
117-
/// </summary>
118124
public static Task<LabResponse> GetSpecificUserAsync(string upn)
119125
{
120126
return GetLabUserDataAsync(new UserQuery() { Upn = upn });

tests/Microsoft.Identity.Test.LabInfrastructure/Microsoft.Identity.Test.LabInfrastructure.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@
88
<ItemGroup>
99
<PackageReference Include="Azure.Security.KeyVault.Certificates" />
1010
<PackageReference Include="Azure.Security.KeyVault.Secrets" />
11+
<PackageReference Include="Azure.Identity" />
12+
<PackageReference Include="Azure.Identity.Broker" />
1113
<PackageReference Include="Newtonsoft.Json" />
1214
</ItemGroup>
1315

1416
<ItemGroup>
17+
<ProjectReference Include="..\..\src\client\Microsoft.Identity.Client.Broker\Microsoft.Identity.Client.Broker.csproj" />
1518
<ProjectReference Include="..\..\src\client\Microsoft.Identity.Client\Microsoft.Identity.Client.csproj">
1619
<Project>{3433eb33-114a-4db7-bc57-14f17f55da3c}</Project>
1720
<Name>Microsoft.Identity.Client</Name>

0 commit comments

Comments
 (0)