Skip to content

Commit b9f12b1

Browse files
Merge branch 'rginsburg/msiv2_feature_branch' into rginsburg/msiv2_probe
2 parents f28b71f + 0b0e18f commit b9f12b1

File tree

8 files changed

+257
-35
lines changed

8 files changed

+257
-35
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
4.73.1
2+
======
3+
4+
### Features
5+
Deprecate AcquireTokenByIntegratedWindowsAuth API in https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/pull/5345
6+
7+
### Bug fixes
8+
Update native interop to 0.19.2 by @fengga in https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/pull/5362 to solve broker bugs
9+
update the deprecated openURL(:) api to openURL(:options:completionHandler) in https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/pull/5354
10+
111
4.73.0
212
=======
313

Directory.Packages.props

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@
8080
<PackageVersion Include="System.ValueTuple" Version="4.5.0" />
8181
<PackageVersion Include="System.Windows.Forms" Version="4.0.0" />
8282
<PackageVersion Include="CommandLineParser" Version="2.8.0" />
83-
<PackageVersion Include="System.Formats.Asn1" Version="9.0.0"/>
84-
83+
<PackageVersion Include="System.Formats.Asn1" Version="9.0.0" />
8584
</ItemGroup>
8685
</Project>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.14.36221.1 d17.14
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MsiSlcPublicApis", "MsiSlcPublicApis\MsiSlcPublicApis.csproj", "{8EF999FD-BFAA-4FA7-BDD7-2720712A6917}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|Any CPU = Debug|Any CPU
11+
Release|Any CPU = Release|Any CPU
12+
EndGlobalSection
13+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
14+
{8EF999FD-BFAA-4FA7-BDD7-2720712A6917}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15+
{8EF999FD-BFAA-4FA7-BDD7-2720712A6917}.Debug|Any CPU.Build.0 = Debug|Any CPU
16+
{8EF999FD-BFAA-4FA7-BDD7-2720712A6917}.Release|Any CPU.ActiveCfg = Release|Any CPU
17+
{8EF999FD-BFAA-4FA7-BDD7-2720712A6917}.Release|Any CPU.Build.0 = Release|Any CPU
18+
EndGlobalSection
19+
GlobalSection(SolutionProperties) = preSolution
20+
HideSolutionNode = FALSE
21+
EndGlobalSection
22+
GlobalSection(ExtensibilityGlobals) = postSolution
23+
SolutionGuid = {07C66F36-6EFF-4CF0-8479-92C14D60BEC9}
24+
EndGlobalSection
25+
EndGlobal
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="Microsoft.Identity.Client" />
12+
<PackageReference Include="Microsoft.IdentityModel.Abstractions" />
13+
</ItemGroup>
14+
15+
</Project>
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using Microsoft.Identity.Client;
5+
using Microsoft.Identity.Client.AppConfig;
6+
using Microsoft.IdentityModel.Abstractions;
7+
using System;
8+
using System.Security.Cryptography.X509Certificates;
9+
using System.Runtime.InteropServices;
10+
using System.Threading.Tasks;
11+
12+
internal class Program
13+
{
14+
private static async Task Main()
15+
{
16+
// ─── 0. Environment banner ────────────────────────────────────────────────
17+
Console.WriteLine(new string('═', 70));
18+
19+
// ─── 1. Certificate events ────────────────────────────────────────────────
20+
ManagedIdentityApplication.BindingCertificateUpdated += c =>
21+
{
22+
Console.WriteLine($"\n[{Now()}] Event Fired - BindingCertificateUpdated:");
23+
PrintCertificate(c);
24+
};
25+
26+
// ─── 2. Managed-identity source & certs ───────────────────────────────────
27+
var source = await ManagedIdentityApplication.GetManagedIdentitySourceAsync()
28+
.ConfigureAwait(false);
29+
Console.WriteLine($"\n[{Now()}] Managed Identity Source: {source}");
30+
31+
var cert = ManagedIdentityApplication.GetManagedIdentityBindingCertificate();
32+
PrintCertificate(cert, "Initial Managed-Identity Binding Certificate");
33+
34+
//sleep for 5 second so we can see different timestamps
35+
Thread.Sleep(5000);
36+
37+
cert = ManagedIdentityApplication.ForceUpdateInMemoryCertificate();
38+
PrintCertificate(cert, "Forced NEW Managed-Identity Binding Certificate");
39+
40+
// ─── 3. Build MI app ──────────────────────────────────────────────────────
41+
IIdentityLogger logger = new IdentityLogger();
42+
IManagedIdentityApplication mi = ManagedIdentityApplicationBuilder
43+
.Create(ManagedIdentityId.SystemAssigned)
44+
.WithExperimentalFeatures()
45+
.WithLogging(logger, true)
46+
.Build();
47+
48+
// ─── 4. Token-acquisition loop ───────────────────────────────────────────
49+
string scope = "https://vault.azure.net/";
50+
while (true)
51+
{
52+
Console.WriteLine($"\n[{Now()}] Acquiring token for scope → {scope}");
53+
try
54+
{
55+
var result = await mi.AcquireTokenForManagedIdentity(scope)
56+
//.WithProofOfPossession()
57+
.ExecuteAsync()
58+
.ConfigureAwait(false);
59+
60+
Console.WriteLine($"[{Now()}] ✅ Success (expires {result.ExpiresOn:HH:mm:ss})");
61+
Console.WriteLine($"Access-Token (first 100 chars): {result.AccessToken[..100]}…");
62+
}
63+
catch (MsalServiceException ex)
64+
{
65+
Console.ForegroundColor = ConsoleColor.Red;
66+
Console.WriteLine($"[{Now()}] ❌ {ex.ErrorCode}: {ex.Message}");
67+
Console.ResetColor();
68+
}
69+
70+
Console.Write("\nEnter new scope or 'q' to quit: ");
71+
var input = Console.ReadLine();
72+
if (string.Equals(input, "q", StringComparison.OrdinalIgnoreCase))
73+
break;
74+
if (!string.IsNullOrWhiteSpace(input))
75+
scope = input.Trim();
76+
}
77+
}
78+
79+
// ─── Helpers ────────────────────────────────────────────────────────────────
80+
81+
private static string Now() => DateTimeOffset.Now.ToString("yyyy-MM-dd HH:mm:ss");
82+
83+
private static void PrintCertificate(X509Certificate2 cert, string? title = null)
84+
{
85+
Console.WriteLine("\n" + (title ?? "Certificate details") + "\n" + new string('-', 60));
86+
Console.WriteLine($"Subject : {cert.Subject}");
87+
Console.WriteLine($"Issuer : {cert.Issuer}");
88+
Console.WriteLine($"Serial # : {cert.SerialNumber}");
89+
Console.WriteLine($"Not Before : {cert.NotBefore:yyyy-MM-dd HH:mm:ss}");
90+
Console.WriteLine($"Not After : {cert.NotAfter:yyyy-MM-dd HH:mm:ss}");
91+
Console.WriteLine($"Thumbprint : {cert.Thumbprint}");
92+
Console.WriteLine(new string('-', 60));
93+
}
94+
}
95+
96+
/// <summary>Minimal ILogger that pipes everything to Console.</summary>
97+
class IdentityLogger : IIdentityLogger
98+
{
99+
public EventLogLevel MinLogLevel => EventLogLevel.Verbose;
100+
public bool IsEnabled(EventLogLevel level) => level <= MinLogLevel;
101+
102+
public void Log(LogEntry entry) => Console.WriteLine($"[{DateTimeOffset.Now:HH:mm:ss}] {entry.Message}");
103+
}

prototype/MsiWithSlc/readme.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Managed Identity – SLC Prototype Demo
2+
3+
This sample showcases **certificate rotation** and **token acquisition** against the **prototype SLC endpoint** using MSAL’s *experimental* Managed-Identity flow.
4+
5+
---
6+
7+
## ✨ What the demo does
8+
9+
1. **Detects the Managed-Identity source** (should be `Credential` when the SLC endpoint is live).
10+
2. **Creates an in-memory binding certificate (`CN=devicecert.mtlsauth.local`).
11+
3. **Raises `BindingCertificateUpdated`** whenever MSAL generates a fresh cert (7-day lifetime in this prototype).
12+
4. **Calls the SLC `/credential` endpoint** over mTLS and exchanges the credential for an **AAD access token** (default scope `https://vault.azure.net/`).
13+
14+
---
15+
16+
## 🛠️ Prerequisites
17+
18+
| Requirement | Version / Notes |
19+
|--------------------|--------------------------------------------|
20+
| .NET SDK | **8.0.100** or later |
21+
| MSAL .NET | **4.72.3 preview** (included via [`Microsoft.Identity.Client`](https://www.nuget.org/packages/Microsoft.Identity.Client/4.72.3-slc-preview)) |
22+
| Access to SLC host | VM/VMSS image with the prototype **SLC agent** enabled |
23+
| Managed Identity | System-assigned (or override in code) |
24+
25+
> **Local run tip**: launch from an Azure VM/VMSS that already has the SLC agent bits; the demo will fail on a dev box without the endpoint.
26+
27+
---
28+
29+
## 🚀 Running the sample
30+
31+
```bash
32+
dotnet restore
33+
dotnet run --configuration Release

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

Lines changed: 69 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -22,44 +22,81 @@ public static class SeleniumExtensions
2222
private static readonly TimeSpan ExplicitTimespan = TimeSpan.FromSeconds(20);
2323
private static readonly TimeSpan ShortExplicitTimespan = TimeSpan.FromSeconds(5);
2424

25-
public static IWebDriver CreateDefaultWebDriver()
25+
public static IWebDriver CreateDefaultWebDriver(int maxRetries = 3, int timeoutSeconds = 30)
2626
{
27-
// ---------- Chrome launch flags ----------
28-
var options = new ChromeOptions();
29-
options.AddArguments(
30-
"--headless=new", // modern headless mode (remove this for debugging)
31-
"--disable-gpu",
32-
"--window-size=1920,1080",
33-
"--remote-allow-origins=*",
34-
"--disable-dev-shm-usage"); // avoids crashes in low-memory containers
35-
36-
// ---------- Pick a driver binary ----------
37-
// 1) Prefer explicit env-var so devs can override locally
38-
var driverDir = Environment.GetEnvironmentVariable("CHROMEWEBDRIVER");
39-
40-
// 2) Otherwise use the folder where CI drops a matching chromedriver
41-
if (string.IsNullOrEmpty(driverDir))
27+
28+
for (int attempt = 1; attempt <= maxRetries; attempt++)
4229
{
43-
driverDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "drivers");
44-
}
30+
try
31+
{
32+
Trace.WriteLine($"[Selenium] Creating Chrome WebDriver (attempt {attempt}/{maxRetries})");
33+
34+
// ---------- Chrome launch flags ----------
35+
var options = new ChromeOptions();
36+
options.AddArguments(
37+
"--headless=new", // modern headless mode (remove this for debugging)
38+
"--disable-gpu",
39+
"--window-size=1920,1080",
40+
"--remote-allow-origins=*",
41+
"--disable-dev-shm-usage"); // avoids crashes in low-memory containers
42+
43+
// Additional options to improve stability
44+
options.PageLoadStrategy = PageLoadStrategy.Eager;
45+
46+
// ---------- Pick a driver binary ----------
47+
// 1) Prefer explicit env-var so devs can override locally
48+
var driverDir = Environment.GetEnvironmentVariable("CHROMEWEBDRIVER");
49+
50+
// 2) Otherwise use the folder where CI drops a matching chromedriver
51+
if (string.IsNullOrEmpty(driverDir))
52+
{
53+
driverDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "drivers");
54+
}
4555

46-
// 3) Fallback: let Selenium look on PATH
47-
ChromeDriverService service = string.IsNullOrEmpty(driverDir)
48-
? ChromeDriverService.CreateDefaultService()
49-
: ChromeDriverService.CreateDefaultService(driverDir);
56+
// 3) Fallback: let Selenium look on PATH
57+
ChromeDriverService service = string.IsNullOrEmpty(driverDir)
58+
? ChromeDriverService.CreateDefaultService()
59+
: ChromeDriverService.CreateDefaultService(driverDir);
5060

51-
service.HideCommandPromptWindow = true;
52-
service.EnableVerboseLogging = true;
61+
service.HideCommandPromptWindow = true;
62+
service.EnableVerboseLogging = true;
63+
64+
var driver = new ChromeDriver(
65+
service,
66+
options,
67+
TimeSpan.FromSeconds(timeoutSeconds));
68+
69+
driver.Manage().Timeouts().ImplicitWait = ImplicitTimespan;
70+
71+
try
72+
{
73+
driver.Navigate().GoToUrl("about:blank");
74+
// If this succeeds, the browser is responding
75+
}
76+
catch (Exception)
77+
{
78+
driver.Dispose();
79+
throw;
80+
}
5381

54-
var driver = new ChromeDriver(
55-
service,
56-
options,
57-
TimeSpan.FromSeconds(120)); // generous startup timeout
82+
TryMaximize(driver);
83+
Trace.WriteLine($"[Selenium] Chrome WebDriver created successfully");
84+
return driver;
85+
}
86+
catch (Exception ex)
87+
{
88+
if (attempt == maxRetries)
89+
{
90+
Trace.WriteLine($"[Selenium] Failed to create Chrome WebDriver after {maxRetries} attempts. Last error: {ex.Message}");
91+
throw;
92+
}
5893

59-
driver.Manage().Timeouts().ImplicitWait = ImplicitTimespan;
94+
Thread.Sleep(1000);
95+
}
96+
}
6097

61-
TryMaximize(driver);
62-
return driver;
98+
// This should never be reached due to the throw in the catch block on the last attempt
99+
throw new InvalidOperationException("Failed to create Chrome WebDriver");
63100
}
64101

65102
private static void TryMaximize(IWebDriver driver)
@@ -311,7 +348,7 @@ private static void EnterUsername(IWebDriver driver, LabUser user, bool withLogi
311348
{
312349
Trace.WriteLine("No, workaround failed");
313350
}
314-
}
351+
}
315352
}
316353
}
317354

tests/Microsoft.Identity.Test.Integration.netcore/SeleniumTests/InteractiveFlowTests.NetFwk.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ private SeleniumWebUI CreateSeleniumCustomWebUIForDuende(string username, string
399399
}
400400

401401
#region Azure AD Kerberos Feature Tests
402-
[IgnoreOnOneBranch]
402+
[Ignore]
403403
public async Task Kerberos_Interactive_AADAsync()
404404
{
405405
LabResponse labResponse = await LabUserHelper.GetDefaultUserAsync().ConfigureAwait(false);

0 commit comments

Comments
 (0)