Skip to content

Commit

Permalink
Update API for managed identity (#4107)
Browse files Browse the repository at this point in the history
* Update API to specify managed identity in .Create()

* Remove cache extensibility from managed identity

* Fix integration tests

* Address comments

* Undo rename of res Id

* Fix tests after merge from main

* Update tests to disable shared cache

* Address comments

* Merge from main

* Remove MIApplicationOptions and address comments

* Rename ManagedIdentityConfiguration to ManagedIdentityId

* Fix tests
  • Loading branch information
neha-bhargava committed May 9, 2023
1 parent 446a348 commit e7533b0
Show file tree
Hide file tree
Showing 29 changed files with 429 additions and 347 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,12 @@ internal override Task<AuthenticationResult> ExecuteInternalAsync(CancellationTo
/// <inheritdoc />
internal override ApiEvent.ApiIds CalculateApiEventId()
{
if (ServiceBundle.Config.IsUserAssignedManagedIdentity)
if (ServiceBundle.Config.ManagedIdentityId.IdType == AppConfig.ManagedIdentityIdType.SystemAssigned)
{
return ApiEvent.ApiIds.AcquireTokenForUserAssignedManagedIdentity;
return ApiEvent.ApiIds.AcquireTokenForSystemAssignedManagedIdentity;
}

return ApiEvent.ApiIds.AcquireTokenForSystemAssignedManagedIdentity;
return ApiEvent.ApiIds.AcquireTokenForUserAssignedManagedIdentity;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,36 @@ internal T WithPlatformProxy(IPlatformProxy platformProxy)
return this as T;
}

/// <summary>
/// Options for MSAL token caches.
///
/// MSAL maintains a token cache internally in memory. By default, this cache object is part of each instance of <see cref="PublicClientApplication"/> or <see cref="ConfidentialClientApplication"/>.
/// This method allows customization of the in-memory token cache of MSAL.
///
/// MSAL's memory cache is different than token cache serialization. Cache serialization pulls the tokens from a cache (e.g. Redis, Cosmos, or a file on disk),
/// where they are stored in JSON format, into MSAL's internal memory cache. Memory cache operations do not involve JSON operations.
///
/// External cache serialization remains the recommended way to handle desktop apps, web site and web APIs, as it provides persistence. These options
/// do not currently control external cache serialization.
///
/// Detailed guidance for each application type and platform:
/// https://aka.ms/msal-net-token-cache-serialization
/// </summary>
/// <param name="options">Options for the internal MSAL token caches. </param>
#if !SUPPORTS_CUSTOM_CACHE || WINDOWS_APP
[EditorBrowsable(EditorBrowsableState.Never)]
#endif
public T WithCacheOptions(CacheOptions options)
{
#if !SUPPORTS_CUSTOM_CACHE || WINDOWS_APP
throw new PlatformNotSupportedException("WithCacheOptions is supported only on platforms where MSAL stores tokens in memory and not on mobile platforms or UWP.");
#else

Config.AccessorOptions = options;
return this as T;
#endif
}

internal T WithUserTokenCacheInternalForTest(ITokenCacheInternal tokenCacheInternal)
{
Config.UserTokenCacheInternalForTest = tokenCacheInternal;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,8 @@ public string ClientVersion

public bool RetryOnServerErrors { get; set; } = true;

public bool IsUserAssignedManagedIdentity { get; internal set; } = false;
public string ManagedIdentityUserAssignedClientId { get; internal set; }
public string ManagedIdentityUserAssignedResourceId { get; internal set; }
public ManagedIdentityId ManagedIdentityId { get; internal set; }

public bool IsManagedIdentity { get; }

public Func<AppTokenProviderParameters, Task<AppTokenProviderResult>> AppTokenProvider;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,36 +83,6 @@ internal T WithHttpManager(IHttpManager httpManager)
return (T)this;
}

/// <summary>
/// Options for MSAL token caches.
///
/// MSAL maintains a token cache internally in memory. By default, this cache object is part of each instance of <see cref="PublicClientApplication"/> or <see cref="ConfidentialClientApplication"/>.
/// This method allows customization of the in-memory token cache of MSAL.
///
/// MSAL's memory cache is different than token cache serialization. Cache serialization pulls the tokens from a cache (e.g. Redis, Cosmos, or a file on disk),
/// where they are stored in JSON format, into MSAL's internal memory cache. Memory cache operations do not involve JSON operations.
///
/// External cache serialization remains the recommended way to handle desktop apps, web site and web APIs, as it provides persistence. These options
/// do not currently control external cache serialization.
///
/// Detailed guidance for each application type and platform:
/// https://aka.ms/msal-net-token-cache-serialization
/// </summary>
/// <param name="options">Options for the internal MSAL token caches. </param>
#if !SUPPORTS_CUSTOM_CACHE || WINDOWS_APP
[EditorBrowsable(EditorBrowsableState.Never)]
#endif
public T WithCacheOptions(CacheOptions options)
{
#if !SUPPORTS_CUSTOM_CACHE || WINDOWS_APP
throw new PlatformNotSupportedException("WithCacheOptions is supported only on platforms where MSAL stores tokens in memory and not on mobile platforms or UWP.");
#else

Config.AccessorOptions = options;
return (T)this;
#endif
}

/// <summary>
/// Sets the logging callback. For details see https://aka.ms/msal-net-logging
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ namespace Microsoft.Identity.Client
/// (for instance a JSON file, as in an asp.net configuration scenario)
/// See https://aka.ms/msal-net-application-configuration
/// See also derived classes <see cref="ApplicationOptions"/>
/// and <see cref="ManagedIdentityApplicationOptions"/>
/// </summary>
public abstract class BaseApplicationOptions
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,107 +32,36 @@ internal ManagedIdentityApplicationBuilder(ApplicationConfiguration configuratio
ApplicationBase.GuardMobileFrameworks();
}

/// <summary>
/// Constructor of a ManagedIdentityApplicationBuilder from application configuration options.
/// See https://aka.ms/msal-net-application-configuration
/// </summary>
/// <param name="options">Managed identity applications configuration options</param>
/// <returns>A <see cref="ManagedIdentityApplicationBuilder"/> from which to set more
/// parameters, and to create a managed identity application instance</returns>
#if !SUPPORTS_CONFIDENTIAL_CLIENT
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] // hide managed identity flow on mobile
#endif
public static ManagedIdentityApplicationBuilder CreateWithApplicationOptions(
ManagedIdentityApplicationOptions options)
{
ApplicationBase.GuardMobileFrameworks();

var config = new ApplicationConfiguration(MsalClientType.ManagedIdentityClient);
var builder = new ManagedIdentityApplicationBuilder(config).WithOptions(options);

if (!string.IsNullOrWhiteSpace(options.UserAssignedClientId))
{
builder = builder.WithUserAssignedManagedIdentity(options.UserAssignedClientId);
}

builder = builder.WithCacheSynchronization(options.EnableCacheSynchronization);

return builder;
}

/// <summary>
/// Creates a ManagedIdentityApplicationBuilder.
/// See https://aka.ms/msal-net-application-configuration
/// </summary>
/// <returns>A <see cref="ManagedIdentityApplicationBuilder"/> from which to set more
/// parameters, and to create a managed identity application instance</returns>
#if !SUPPORTS_CONFIDENTIAL_CLIENT
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] // hide managed identity flow on mobile
#endif
public static ManagedIdentityApplicationBuilder Create()
{
ApplicationBase.GuardMobileFrameworks();

var config = new ApplicationConfiguration(MsalClientType.ManagedIdentityClient);
return new ManagedIdentityApplicationBuilder(config)
.WithCacheSynchronization(false);
}

/// <summary>
/// Creates a ManagedIdentityApplicationBuilder from a user assigned managed identity clientID / resourceId.
/// See https://aka.ms/msal-net-managed-identity
/// For example, for a system assigned managed identity use ManagedIdentityApplicationBuilder.Create(ManagedIdentityId.SystemAssigned)
/// and for a user assigned managed identity use ManagedIdentityApplicationBuilder.Create(ManagedIdentityId.WithUserAssignedClientId(clientId)).
/// For more details see https://aka.ms/msal-net-managed-identity
/// </summary>
/// <param name="userAssignedId">Client ID / Resource ID of the user assigned managed identity assigned to the resource.</param>
/// <param name="managedIdentityId">Configuration of the Managed Identity assigned to the resource.</param>
/// <returns>A <see cref="ManagedIdentityApplicationBuilder"/> from which to set more
/// parameters, and to create a managed identity application instance</returns>
#if !SUPPORTS_CONFIDENTIAL_CLIENT
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] // hide confidential client on mobile
#endif
public static ManagedIdentityApplicationBuilder Create(string userAssignedId)
public static ManagedIdentityApplicationBuilder Create(ManagedIdentityId managedIdentityId)
{
ApplicationBase.GuardMobileFrameworks();

if (string.IsNullOrWhiteSpace(userAssignedId))
{
throw new ArgumentNullException(nameof(userAssignedId));
}

var config = new ApplicationConfiguration(MsalClientType.ManagedIdentityClient);
return new ManagedIdentityApplicationBuilder(config)
.WithUserAssignedManagedIdentity(userAssignedId)
.WithCacheSynchronization(false);
return new ManagedIdentityApplicationBuilder(BuildConfiguration(managedIdentityId));
}

private ManagedIdentityApplicationBuilder WithUserAssignedManagedIdentity(string userAssignedId)
private static ApplicationConfiguration BuildConfiguration(ManagedIdentityId managedIdentityId)
{
Config.IsUserAssignedManagedIdentity = true;
_ = managedIdentityId ?? throw new ArgumentNullException(nameof(managedIdentityId));
var config = new ApplicationConfiguration(MsalClientType.ManagedIdentityClient);

if (Guid.TryParse(userAssignedId, out _))
{
Config.ManagedIdentityUserAssignedClientId = userAssignedId;
}
else
{
Config.ManagedIdentityUserAssignedResourceId = userAssignedId;
}
config.ManagedIdentityId = managedIdentityId;

return this;
}
config.CacheSynchronizationEnabled = false;
config.AccessorOptions = CacheOptions.EnableSharedCacheOptions;

/// <summary>
/// When set to <c>true</c>, MSAL will lock cache access at the <see cref="ManagedIdentityApplication"/> level, i.e.
/// the block of code between BeforeAccessAsync and AfterAccessAsync callbacks will be synchronized.
/// Apps can set this flag to <c>false</c> to enable an optimistic cache locking strategy, which may result in better performance, especially
/// when ConfidentialClientApplication or ManagedIdentityApplication objects are reused.
/// </summary>
/// <remarks>
/// False by default.
/// Not recommended for apps that call RemoveAsync
/// </remarks>
public ManagedIdentityApplicationBuilder WithCacheSynchronization(bool enableCacheSynchronization)
{
Config.CacheSynchronizationEnabled = enableCacheSynchronization;
return this;
return config;
}

/// <summary>
Expand Down Expand Up @@ -201,7 +130,7 @@ public IManagedIdentityApplication Build()
/// <returns></returns>
internal ManagedIdentityApplication BuildConcrete()
{
ValidateUseOfExperimentalFeature("ManagedIdentityClient");
ValidateUseOfExperimentalFeature("ManagedIdentity");
DefaultConfiguration();
return new ManagedIdentityApplication(BuildConfiguration());
}
Expand All @@ -217,17 +146,13 @@ private void DefaultConfiguration()

private void ComputeClientIdForCaching()
{
if (!string.IsNullOrEmpty(Config.ManagedIdentityUserAssignedClientId))
{
Config.ClientId = Config.ManagedIdentityUserAssignedClientId;
}
else if (!string.IsNullOrEmpty(Config.ManagedIdentityUserAssignedResourceId))
if (Config.ManagedIdentityId.IdType == ManagedIdentityIdType.SystemAssigned)
{
Config.ClientId = Config.ManagedIdentityUserAssignedResourceId;
Config.ClientId = Constants.ManagedIdentityDefaultClientId;
}
else
{
Config.ClientId = Constants.ManagedIdentityDefaultClientId;
Config.ClientId = Config.ManagedIdentityId.UserAssignedId;
}
}
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Microsoft.Identity.Client.AppConfig
{
/// <summary>
/// Class to store configuration for a managed identity enabled on a resource.
/// For a system assigned managed identity use ManagedIdentityId.SystemAssigned.
/// For user assigned managed identity use ManagedIdentityId.WithUserAssignedClientId("clientId") or
/// ManagedIdentityId.WithUserAssignedResourceId("resourceId").
/// For more details see https://aka.ms/msal-net-managed-identity
/// </summary>
public class ManagedIdentityId
{
internal string UserAssignedId { get; private set; }
internal ManagedIdentityIdType IdType { get; private set; }
internal readonly bool _isUserAssigned;

private ManagedIdentityId(ManagedIdentityIdType idType)
{
IdType = idType;

switch (idType)
{
case ManagedIdentityIdType.SystemAssigned:
_isUserAssigned = false;
break;
case ManagedIdentityIdType.ClientId:
case ManagedIdentityIdType.ResourceId:
_isUserAssigned = true;
break;
}
}

/// <summary>
/// Create an instance of ManagedIdentityId for a system assigned managed identity.
/// </summary>
public static ManagedIdentityId SystemAssigned { get; } =
new ManagedIdentityId(ManagedIdentityIdType.SystemAssigned);

/// <summary>
/// Create an instance of ManagedIdentityId for a user assigned managed identity from a client id.
/// </summary>
/// <param name="clientId">Client id of the user assigned managed identity assigned to azure resource.</param>
/// <returns>Instance of ManagedIdentityId.</returns>
/// <exception cref="ArgumentNullException"></exception>
public static ManagedIdentityId WithUserAssignedClientId(string clientId)
{
if (string.IsNullOrEmpty(clientId))
{
throw new ArgumentNullException(clientId);
}

return new ManagedIdentityId(ManagedIdentityIdType.ClientId) { UserAssignedId = clientId };
}

/// <summary>
/// Create an instance of ManagedIdentityId for a user assigned managed identity from a resource id.
/// </summary>
/// <param name="resourceId">Resource id of the user assigned managed identity assigned to azure resource.</param>
/// <returns>Instance of ManagedIdentityId.</returns>
/// <exception cref="ArgumentNullException"></exception>
public static ManagedIdentityId WithUserAssignedResourceId(string resourceId)
{
if (string.IsNullOrEmpty(resourceId))
{
throw new ArgumentNullException(resourceId);
}

return new ManagedIdentityId(ManagedIdentityIdType.ResourceId) { UserAssignedId = resourceId };
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Microsoft.Identity.Client.AppConfig
{
internal enum ManagedIdentityIdType
{
SystemAssigned,
ClientId,
ResourceId
}
}

0 comments on commit e7533b0

Please sign in to comment.