Azure / azure-sdk-for-net

This repository is for active development of the Azure SDK for .NET. For consumers of the SDK we recommend visiting our public developer docs at https://learn.microsoft.com/dotnet/azure/ or our versioned developer docs at https://azure.github.io/azure-sdk-for-net.
MIT License
5.13k stars 4.53k forks source link

[FEATURE REQ] Support for federated identity credential + managed identity #44463

Open yhvicey opened 3 weeks ago

yhvicey commented 3 weeks ago

Library name

Azure.Identity

Please describe the feature.

Currently Azure.Identity lib supports using federated identity credetial + workload identity to achieve cross-tenant authentication for AKS workloads, however for general manage identity cases is there any plan to add such support?

With this feature we'll be able to use Azure.Identity anywhere that supports managed identity, then using federated identity credentials to support cross-tenant credential-free access.

Basically we'll have a new credentials object like below:

public class FederatedManagedIdentityCredentialOptions : TokenCredentialOptions
{
    public string TenantId { get; set; } = EnvironmentVariables.TenantId;
    public string ClientId { get; set; } = EnvironmentVariables.ClientId;
    public string FederatedClientId { get; set; } = EnvironmentVariables.AzureFederatedClientId;
}

public class FederatedManagedIdentityCredential : TokenCredential
{
    private readonly ClientAssertionCredential _clientAssertionCredential;

    public FederatedManagedIdentityCredential() : this(default) { }

    public FederatedManagedIdentityCredential(FederatedManagedIdentityCredentialOptions options)
    {
        options = options ?? new();

        if (!string.IsNullOrEmpty(options.TenantId) && !string.IsNullOrEmpty(options.ClientId) && !string.IsNullOrEmpty(options.FederatedClientId))
        {
            ClientAssertionCredentialOptions clientAssertionCredentialOptions = options.Clone<ClientAssertionCredentialOptions>();

            _clientAssertionCredential = new ClientAssertionCredential(
                options.TenantId,
                options.ClientId,
                async (cancellationToken) =>
                {
                    ManagedIdentityCredential miCredential = new ManagedIdentityCredential(options.FederatedClientId);
                    AccessToken token = await miCredential.GetTokenAsync(
                        new TokenRequestContext(new string[] { $"api://AzureADTokenExchange/.default" }),
                        cancellationToken).ConfigureAwait(false);
                    return token.Token;
                },
                clientAssertionCredentialOptions);
        }
    }

    public override AccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken = default)
    {
        return GetTokenCoreAsync(false, requestContext, cancellationToken).EnsureCompleted();
    }

    public override async ValueTask<AccessToken> GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancellationToken = default)
    {
        return await GetTokenCoreAsync(true, requestContext, cancellationToken).ConfigureAwait(false);
    }

    private async ValueTask<AccessToken> GetTokenCoreAsync(bool async, TokenRequestContext requestContext, CancellationToken cancellationToken)
    {
        using CredentialDiagnosticScope scope = _pipeline.StartGetTokenScope("FederatedManagedIdentityCredential.GetToken", requestContext);

        try
        {
            if (_clientAssertionCredential == null)
            {
                throw new CredentialUnavailableException("Failed");
            }

            AccessToken token = async ? await _clientAssertionCredential.GetTokenAsync(requestContext, cancellationToken).ConfigureAwait(false)
                : _clientAssertionCredential.GetToken(requestContext, cancellationToken);

            return scope.Succeeded(token);
        }
        catch (Exception e)
        {
            throw scope.FailWrapAndThrow(e);
        }
    }
}
github-actions[bot] commented 3 weeks ago

Thank you for your feedback. Tagging and routing to the team member best able to assist.