|
13 | 13 | using GitCredentialManager.UI;
|
14 | 14 | using GitCredentialManager.UI.ViewModels;
|
15 | 15 | using GitCredentialManager.UI.Views;
|
| 16 | +using Microsoft.Identity.Client.AppConfig; |
16 | 17 |
|
17 | 18 | #if NETFRAMEWORK
|
18 | 19 | using System.Drawing;
|
@@ -44,6 +45,25 @@ Task<IMicrosoftAuthenticationResult> GetTokenForUserAsync(string authority, stri
|
44 | 45 | /// <param name="scopes">Scopes to request.</param>
|
45 | 46 | /// <returns>Authentication result.</returns>
|
46 | 47 | Task<IMicrosoftAuthenticationResult> GetTokenForServicePrincipalAsync(ServicePrincipalIdentity sp, string[] scopes);
|
| 48 | + |
| 49 | + /// <summary> |
| 50 | + /// Acquire a token using the managed identity in the current environment. |
| 51 | + /// </summary> |
| 52 | + /// <param name="managedIdentity">Managed identity to use.</param> |
| 53 | + /// <param name="resource">Resource to obtain an access token for.</param> |
| 54 | + /// <returns>Authentication result including access token.</returns> |
| 55 | + /// <remarks> |
| 56 | + /// There are several formats for the <paramref name="managedIdentity"/> parameter: |
| 57 | + /// <para/> |
| 58 | + /// - <c>"system"</c> - Use the system-assigned managed identity. |
| 59 | + /// <para/> |
| 60 | + /// - <c>"{guid}"</c> - Use the user-assigned managed identity with client ID <c>{guid}</c>. |
| 61 | + /// <para/> |
| 62 | + /// - <c>"id://{guid}"</c> - Use the user-assigned managed identity with client ID <c>{guid}</c>. |
| 63 | + /// <para/> |
| 64 | + /// - <c>"resource://{guid}"</c> - Use the user-assigned managed identity with resource ID <c>{guid}</c>. |
| 65 | + /// </remarks> |
| 66 | + Task<IMicrosoftAuthenticationResult> GetTokenForManagedIdentityAsync(string managedIdentity, string resource); |
47 | 67 | }
|
48 | 68 |
|
49 | 69 | public class ServicePrincipalIdentity
|
@@ -265,6 +285,31 @@ public async Task<IMicrosoftAuthenticationResult> GetTokenForServicePrincipalAsy
|
265 | 285 | }
|
266 | 286 | }
|
267 | 287 |
|
| 288 | + public async Task<IMicrosoftAuthenticationResult> GetTokenForManagedIdentityAsync(string managedIdentity, string resource) |
| 289 | + { |
| 290 | + var httpFactoryAdaptor = new MsalHttpClientFactoryAdaptor(Context.HttpClientFactory); |
| 291 | + |
| 292 | + ManagedIdentityId mid = GetManagedIdentity(managedIdentity); |
| 293 | + |
| 294 | + IManagedIdentityApplication app = ManagedIdentityApplicationBuilder.Create(mid) |
| 295 | + .WithHttpClientFactory(httpFactoryAdaptor) |
| 296 | + .Build(); |
| 297 | + |
| 298 | + try |
| 299 | + { |
| 300 | + AuthenticationResult result = await app.AcquireTokenForManagedIdentity(resource).ExecuteAsync(); |
| 301 | + return new MsalResult(result); |
| 302 | + } |
| 303 | + catch (Exception ex) |
| 304 | + { |
| 305 | + Context.Trace.WriteLine(mid == ManagedIdentityId.SystemAssigned |
| 306 | + ? "Failed to acquire token for system managed identity." |
| 307 | + : $"Failed to acquire token for user managed identity '{managedIdentity:D}'."); |
| 308 | + Context.Trace.WriteException(ex); |
| 309 | + throw; |
| 310 | + } |
| 311 | + } |
| 312 | + |
268 | 313 | private async Task<bool> UseDefaultAccountAsync(string userName)
|
269 | 314 | {
|
270 | 315 | ThrowIfUserInteractionDisabled();
|
@@ -624,6 +669,50 @@ internal StorageCreationProperties CreateUserTokenCacheProps(bool useLinuxFallba
|
624 | 669 | return builder.Build();
|
625 | 670 | }
|
626 | 671 |
|
| 672 | + internal static ManagedIdentityId GetManagedIdentity(string str) |
| 673 | + { |
| 674 | + // An empty string or "system" means system-assigned managed identity |
| 675 | + if (string.IsNullOrWhiteSpace(str) || str.Equals("system", StringComparison.OrdinalIgnoreCase)) |
| 676 | + { |
| 677 | + return ManagedIdentityId.SystemAssigned; |
| 678 | + } |
| 679 | + |
| 680 | + // |
| 681 | + // A GUID-looking value means a user-assigned managed identity specified by the client ID. |
| 682 | + // If the "{value}" is the empty GUID then we use the system-assigned MI. |
| 683 | + // |
| 684 | + if (Guid.TryParse(str, out Guid guid)) |
| 685 | + { |
| 686 | + return guid == Guid.Empty |
| 687 | + ? ManagedIdentityId.SystemAssigned |
| 688 | + : ManagedIdentityId.WithUserAssignedClientId(str); |
| 689 | + } |
| 690 | + |
| 691 | + // |
| 692 | + // A value of the form "id://{value}" means a user-assigned managed identity specified by the client ID. |
| 693 | + // If the "{value}" is the empty GUID then we use the system-assigned MI. |
| 694 | + // |
| 695 | + // If the value is "resource://{value}" then it is a user-assigned managed identity specified |
| 696 | + // by the resource ID. |
| 697 | + // |
| 698 | + if (Uri.TryCreate(str, UriKind.Absolute, out Uri uri)) |
| 699 | + { |
| 700 | + if (StringComparer.OrdinalIgnoreCase.Equals(uri.Scheme, "id")) |
| 701 | + { |
| 702 | + return Guid.TryParse(uri.Host, out Guid g) && g == Guid.Empty |
| 703 | + ? ManagedIdentityId.SystemAssigned |
| 704 | + : ManagedIdentityId.WithUserAssignedClientId(uri.Host); |
| 705 | + } |
| 706 | + |
| 707 | + if (StringComparer.OrdinalIgnoreCase.Equals(uri.Scheme, "resource")) |
| 708 | + { |
| 709 | + return ManagedIdentityId.WithUserAssignedResourceId(uri.Host); |
| 710 | + } |
| 711 | + } |
| 712 | + |
| 713 | + throw new ArgumentException("Invalid managed identity value.", nameof(str)); |
| 714 | + } |
| 715 | + |
627 | 716 | private static EmbeddedWebViewOptions GetEmbeddedWebViewOptions()
|
628 | 717 | {
|
629 | 718 | return new EmbeddedWebViewOptions
|
@@ -774,7 +863,7 @@ public MsalResult(AuthenticationResult msalResult)
|
774 | 863 | }
|
775 | 864 |
|
776 | 865 | public string AccessToken => _msalResult.AccessToken;
|
777 |
| - public string AccountUpn => _msalResult.Account.Username; |
| 866 | + public string AccountUpn => _msalResult.Account?.Username; |
778 | 867 | }
|
779 | 868 |
|
780 | 869 | #if NETFRAMEWORK
|
|
0 commit comments