bdcoder
bdcoder

Reputation: 3781

Visual Studio 2019 TokenService.exe has failed with unexpected error: TS003: Error, TS004: Unable to get access token

Environment:

Microsoft Visual Studio Community 2019
Version 16.11.9
VisualStudio.16.Release/16.11.9+32106.194

Target framework is .NET 5.0

Trying to test LOCALLY while establishing a connection to our Azure Keyvault using Visual Studio. Using the new Azure Identity client library.

NuGet packages installed:

The relevant code is shown below:

using Azure.Identity;

Microsoft.Extensions.Configuration.ConfigurationBuilder config_builder;

Azure.Identity.DefaultAzureCredentialOptions default_azure_credential_options;

Azure.Identity.DefaultAzureCredential azure_credential_default;


// Exclude all to begin with ...

default_azure_credential_options = new DefaultAzureCredentialOptions
   {

   ExcludeAzureCliCredential = true,
   ExcludeAzurePowerShellCredential = true,
   ExcludeEnvironmentCredential = true,
   ExcludeInteractiveBrowserCredential = true,
   ExcludeManagedIdentityCredential = true,
   ExcludeSharedTokenCacheCredential = true,
   ExcludeVisualStudioCodeCredential = true,
   ExcludeVisualStudioCredential = true

   };


// Try to use the Visual Studio credential ...

default_azure_credential_options.ExcludeVisualStudioCredential = false;

// Create credentials and add Azure KeyVault config keys / values ...

azure_credential_default = new DefaultAzureCredential( default_azure_credential_options );

config_builder.AddAzureKeyVault( keyvault_uri, azure_credential_default );

...

config_builder.Build();

As per the docs, I am signed-in to our Azure account via Visual Studio.

Also, as per the docs, when this error occurs, I sign-out and sign-in again to our Azure account (again via Visual Studio). However, when the Build() method is called, the following exception is raised:

Azure.Identity.CredentialUnavailableException: Process "D:\apps\VStudioNet2019\Common7\IDE\Extensions\rahssdlq.j5o\TokenService\Microsoft.Asal.TokenService.exe" has failed with unexpected error: TS003: Error, TS004: Unable to get access token.  'AADSTS50020: User account '{EmailHidden}' from identity provider 'live.com' does not exist in tenant 'Microsoft Services' and cannot access the application '872cd9fa-d31f-45e0-9eab-6e460a02d1f1'(Visual Studio) in that tenant. The account needs to be added as an external user in the tenant first. Sign out and sign in again with a different Azure Active Directory user account.
Trace ID: 819fe58d-fe72-4688-9750-d88409882f00
Correlation ID: 7443c9c8-757a-44af-992d-b2576add5941
Timestamp: 2022-01-26 19:24:16Z'.
 ---> System.InvalidOperationException: TS003: Error, TS004: Unable to get access token.  'AADSTS50020: User account '{EmailHidden}' from identity provider 'live.com' does not exist in tenant 'Microsoft Services' and cannot access the application '872cd9fa-d31f-45e0-9eab-6e460a02d1f1'(Visual Studio) in that tenant. The account needs to be added as an external user in the tenant first. Sign out and sign in again with a different Azure Active Directory user account.
Trace ID: 819fe58d-fe72-4688-9750-d88409882f00
Correlation ID: 7443c9c8-757a-44af-992d-b2576add5941
Timestamp: 2022-01-26 19:24:16Z'
   at Azure.Identity.VisualStudioCredential.RunProcessesAsync(List`1 processStartInfos, Boolean async, CancellationToken cancellationToken)
   --- End of inner exception stack trace ---
   at Azure.Identity.DefaultAzureCredential.GetTokenFromSourcesAsync(TokenCredential[] sources, TokenRequestContext requestContext, Boolean async, CancellationToken cancellationToken)
   at Azure.Identity.DefaultAzureCredential.GetTokenImplAsync(Boolean async, TokenRequestContext requestContext, CancellationToken cancellationToken)
   at Azure.Identity.CredentialDiagnosticScope.FailWrapAndThrow(Exception ex, String additionalMessage)
   at Azure.Identity.DefaultAzureCredential.GetTokenImplAsync(Boolean async, TokenRequestContext requestContext, CancellationToken cancellationToken)
   at Azure.Identity.DefaultAzureCredential.GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancellationToken)
   at Azure.Security.KeyVault.ChallengeBasedAuthenticationPolicy.AuthenticateRequestAsync(HttpMessage message, Boolean async)
   at Azure.Security.KeyVault.ChallengeBasedAuthenticationPolicy.ProcessCoreAsync(HttpMessage message, ReadOnlyMemory`1 pipeline, Boolean async)
   at Azure.Core.Pipeline.RedirectPolicy.ProcessAsync(HttpMessage message, ReadOnlyMemory`1 pipeline, Boolean async)
   at Azure.Core.Pipeline.RetryPolicy.ProcessAsync(HttpMessage message, ReadOnlyMemory`1 pipeline, Boolean async)
   at Azure.Core.Pipeline.RetryPolicy.ProcessAsync(HttpMessage message, ReadOnlyMemory`1 pipeline, Boolean async)
   at Azure.Core.Pipeline.HttpPipeline.SendRequestAsync(Request request, CancellationToken cancellationToken)
   at Azure.Security.KeyVault.KeyVaultPipeline.SendRequestAsync(Request request, CancellationToken cancellationToken)
   at Azure.Security.KeyVault.KeyVaultPipeline.GetPageAsync[T](Uri firstPageUri, String nextLink, Func`1 itemFactory, String operationName, CancellationToken cancellationToken)
   at Azure.Core.PageResponseEnumerator.FuncAsyncPageable`1.AsPages(String continuationToken, Nullable`1 pageSizeHint)+MoveNext()
   at Azure.Core.PageResponseEnumerator.FuncAsyncPageable`1.AsPages(String continuationToken, Nullable`1 pageSizeHint)+System.Threading.Tasks.Sources.IValueTaskSource<System.Boolean>.GetResult()
   at Azure.AsyncPageable`1.GetAsyncEnumerator(CancellationToken cancellationToken)+MoveNext()
   at Azure.AsyncPageable`1.GetAsyncEnumerator(CancellationToken cancellationToken)+MoveNext()
   at Azure.AsyncPageable`1.GetAsyncEnumerator(CancellationToken cancellationToken)+System.Threading.Tasks.Sources.IValueTaskSource<System.Boolean>.GetResult()
   at Azure.Extensions.AspNetCore.Configuration.Secrets.AzureKeyVaultConfigurationProvider.LoadAsync()
   at Azure.Extensions.AspNetCore.Configuration.Secrets.AzureKeyVaultConfigurationProvider.LoadAsync()
   at Azure.Extensions.AspNetCore.Configuration.Secrets.AzureKeyVaultConfigurationProvider.Load()
   at Microsoft.Extensions.Configuration.ConfigurationRoot..ctor(IList`1 providers)
   at Microsoft.Extensions.Configuration.ConfigurationBuilder.Build()

Have consulted and tried the following (none of which has helped or worked):

Does anyone have a working example (step-by-step) of how to authenticate using the VisualStudio credential?

As a side note, if I set the ExcludeAzureCliCredential = false, and log in to our Azure account via the Azure CLI, everything works -- but I still would like to know HOW to make the VisualStudio credential option work.

Update: 27-Jan-2022 ...

Was finally able to get the Visual Studio credential working -- HOWEVER, it requires supplying the tenant ID. The modified code is:

Microsoft.Extensions.Configuration.ConfigurationBuilder config_builder;

Azure.Identity.DefaultAzureCredentialOptions default_azure_credential_options;

Azure.Identity.DefaultAzureCredential azure_credential_default;


// Exclude all to begin with ...

default_azure_credential_options = new DefaultAzureCredentialOptions
   {

   ExcludeAzureCliCredential = true,
   ExcludeAzurePowerShellCredential = true,
   ExcludeEnvironmentCredential = true,
   ExcludeInteractiveBrowserCredential = true,
   ExcludeManagedIdentityCredential = true,
   ExcludeSharedTokenCacheCredential = true,
   ExcludeVisualStudioCodeCredential = true,
   ExcludeVisualStudioCredential = true

   };


// Try to use the Visual Studio credential ...

default_azure_credential_options.ExcludeVisualStudioCredential = false;


/*
The tenant ID of the user to authenticate.  The default is null and will authenticate users to 
their default tenant. The value can also be set by setting the environment variable AZURE_TENANT_ID.
Here we set the value explicitly.  The value was obtained AFTER logging into Azure via the CLI, i.e.:
 > az login
 > az account list
   [
    {
     ...
     "tenantId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
     ...
    }
   ]
*/

default_azure_credential_options.VisualStudioTenantId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";

// Create credentials and add Azure KeyVault config keys / values ...

azure_credential_default = new DefaultAzureCredential( default_azure_credential_options );

config_builder.AddAzureKeyVault( keyvault_uri, azure_credential_default );

...

config_builder.Build();

The above code does work, however, I cannot seem to find anything in the official docs that state the Tenant ID is required. From what I have read, the credentials should be picked up AUTOMATICALLY after logging into Azure via Visual Studio IDE (but that does not work). See the section Authenticating via Visual Studio from the official docs at Azure Identity client library for .NET - Version 1.5.0 - nothing is mentioned that a Tenant ID is required.

Thanks in advance.

Upvotes: 4

Views: 5435

Answers (2)

ZakiMa
ZakiMa

Reputation: 6241

I had the same error with both VS 2019 and VS 2022 but due to different reason. My application used credentials to talk to Azure Storage plus it was getting tokens for my scope.

Neither of above steps helped (neither explicitly setting Tenant Id nor adding Azure.Security.KeyVault.Secrets package).

    private async Task RetrieveAccessToken()
    {
        TokenRequestContext tokenRequestContext = new TokenRequestContext(new[] { scope });
        this.AccessToken = await this.tokenCredential.GetTokenAsync(tokenRequestContext, CancellationToken.None);
    }

Initially I created one instance of DefaultAzureCredential which resulted in this error. At the end what helped is to extract logic of its creation and pass independent instances where needed:

    private static TokenCredential CreateCredential(string managedIdentityClientId)
    {
        TokenCredential tokenCredential;
        if (string.Equals(managedIdentityClientId, "dev", StringComparison.OrdinalIgnoreCase))
        {
            tokenCredential = new DefaultAzureCredential();
        }
        else
        {
            tokenCredential = new ManagedIdentityCredential(managedIdentityClientId);
        }

        return tokenCredential;
    }

I'm not sure why it behaves this way. Note, Azure Storage still was able to reuse (I have two layers which required it). But requesting tokens for my custom scope required fresh instance.

This is not the case for ManagedIdentityCredential - that one worked just fine with the single instance reused.

Upvotes: 0

bdcoder
bdcoder

Reputation: 3781

Thanks to @Christopher Scott - the solution was to install the following NuGet package:

PM> Install-Package Azure.Security.KeyVault.Secrets -Version 4.3.0-beta.4

The above package "has a new feature that will attempt to perform tenant discovery".

Once the above package was included in the project, I was able to run the application locally using Visual Studio credentials WITHOUT having to explicitly set the Tenant ID.

Hopefully, the Azure.Extensions.AspNetCore.Configuration.Secrets package will be updated soon to make use of the newer Azure.Security.KeyVault.Secrets package (which it depends on), then the original two NuGet packages will only need to be used (as per the original post).

Upvotes: 3

Related Questions