AzureAD / azure-activedirectory-identitymodel-extensions-for-dotnet

IdentityModel extensions for .Net
MIT License
1.04k stars 390 forks source link

Using DefaultAzureCredential (new Azure SDK) with ManagedKeyVaultSecurityKey not possible? #1583

Closed aKzenT closed 2 weeks ago

aKzenT commented 3 years ago

We are currently moving our solution to the new Azure SDK which no longer uses Authentication-Callback but rather CredentialToken-classes that allow for various authentication methods. This is very convenient for us as it enables automatic support for development credentials obtained from Visual Studio as well as service credentials obtained from a managed identity.

Unfortunately I did not find any way to integrate this with the Microsoft.IdentityModel.ManagedKeyVaultSecurityKey package.

Is there any way to make them compatible? Are there plans to release a new version of this library that supports the new Azure SDK?

I need the SecurityKey to validate Json Web Tokens. I didn't find a way to do this directly with the new SDK either.

brentschmaltz commented 3 years ago

@aKzenT thanks for pointing this out, will investigate.

sizzle168 commented 3 years ago

Any updates on this? I'm having the same problem.

mhusseini commented 3 years ago

+1. I'd be interested in a solution as well.

jmprieur commented 3 years ago

@mhusseini @aKzenT : if you are on ASP.NET Core, did you consider Microsoft.Identity.Web ? (which does use it already)

aKzenT commented 3 years ago

From what I see in Microsoft.Identity.Web it does not fit my needs. I want to validate, decrypt and read the content of JsonWebTokens which are encrypted using KeyVault. I'm not using the tokens for authentication directly.

If I'm mistaken, I would apreciate a sample of how to decrypt tokens using the library.

aKzenT commented 1 year ago

@brentschmaltz any update on this?

brentschmaltz commented 1 year ago

@aKzenT can you point us to the code you are trying to make work?

aKzenT commented 1 year ago

Sure. We are passing tokens from one website to another. The keys are encrypted using a key that is persisted in the Azure KeyVault. To validate/decrypt the tokens, we basically use something like:

        KeyVaultSecurityKey2 key = new KeyVaultSecurityKey2(uri);
        key.CryptoProviderFactory.CustomCryptoProvider = new KeyVaultCryptoProvider2(tokenCredential);

        var handler = new JwtSecurityTokenHandler();

        handler.ValidateToken(
            request.Token,
            new TokenValidationParameters
            {
                ...
                TokenDecryptionKey = key,
            },
            out SecurityToken securityToken);

tokenCredential is of type Azure.Core.TokenCredential, which are the newer SDKs, which support authentication with credentials saved in Visual Studio, Azure CLI or other. See https://docs.microsoft.com/en-us/dotnet/api/azure.identity.defaultazurecredential .

KeyVaultSecurityKey2 and KeyVaultCryptoProvider2 are our own implementations that use the newer Azure KeyVault APIs to provide keys. This is what we would like to see integrated directly in this SDK, which currently only supports the old type of authentication using callback.

jmprieur commented 1 year ago

@aKzenT Identity.Model cannot depend on DefaultAzureCredential, which itself depends on MSAL.NET We will think of a solution, though

cc: @brentschmaltz

aKzenT commented 1 year ago

For the credentials you would only need to depend on Azure.Core which contains the TokenCredential base class and does not have any dependency besides the BCL: https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/core/Azure.Core/src/Azure.Core.csproj

For the actual crypto implementaton we use CryptographyClient from Azure.Security.KeyVault.Keys project, which does not seem to depend on MSAL.NET either: https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Azure.Security.KeyVault.Keys.csproj

jmprieur commented 1 year ago

That's right, @aKzenT: we agree. We don't want to depend on DefaultAzureCredential (your original request)

bertnz commented 1 year ago

Hi,

We are attempting to switch our Kubernetes Pods to use Azure Workload Identity but have hit a snag with this library also.

We were trying to use the AuthenticationCallBack to see if we could somehow get this library to auth with the new DefaultAzureCredentials like so.

var key = new ManagedKeyVaultSecurityKey(keyIdentifier, GetKeyVaultTokenUsingAzureIdentity)
{
    CryptoProviderFactory = new CryptoProviderFactory() { CustomCryptoProvider = new KeyVaultCryptoProvider() }
};

with the callback implemented as so

private static async Task<string> GetKeyVaultTokenUsingAzureIdentity(string authority, string resource, string scope)
{
    var tokenTask = await new DefaultAzureCredential().GetTokenAsync(new TokenRequestContext(new[] { scope })).ConfigureAwait(false);
    return tokenTask.Token;
}

This is failing for us with the following stacktrace

 ClientAssertionCredential authentication failed: The cache contains multiple tokens satisfying the requirements. Try to clear token cache. ,Azure.Identity.AuthenticationFailedException,System.Exception FailWrapAndThrow(System.Exception, System.String),   at Exception Azure.Identity.CredentialDiagnosticScope.FailWrapAndThrow(Exception ex, string additionalMessage)
   at AccessToken Azure.Identity.ClientAssertionCredential.GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken)
   at async ValueTask<AccessToken> Azure.Identity.TokenExchangeManagedIdentitySource.AuthenticateAsync(bool async, TokenRequestContext context, CancellationToken cancellationToken)
   at async ValueTask<AccessToken> Azure.Identity.ManagedIdentityClient.AuthenticateAsync(bool async, TokenRequestContext context, CancellationToken cancellationToken)
   at async ValueTask<AccessToken> Azure.Identity.ManagedIdentityCredential.GetTokenImplAsync(bool async, TokenRequestContext requestContext, CancellationToken cancellationToken)
   at Exception Azure.Identity.CredentialDiagnosticScope.FailWrapAndThrow(Exception ex, string additionalMessage)
   at async ValueTask<AccessToken> Azure.Identity.ManagedIdentityCredential.GetTokenImplAsync(bool async, TokenRequestContext requestContext, CancellationToken cancellationToken)
   at AccessToken Azure.Identity.ManagedIdentityCredential.GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken)

Is this approach even feasible?

keegan-caruso commented 2 weeks ago

ManagedKeyVaultSecurityKey is no longer supported in 8x.