Skip to content

Commit 12ff075

Browse files
authored
VisualStudioCredential falls through to next chained cred when no account is found (Azure#48469)
1 parent 53c7a39 commit 12ff075

File tree

3 files changed

+24
-1
lines changed

3 files changed

+24
-1
lines changed

sdk/identity/Azure.Identity/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
### Breaking Changes
88

99
### Bugs Fixed
10+
- `VisualStudioCredential` will now correctly fall through to the next credential in the chain when no account is found by Visual Studio. ([#48464](https://github.com/Azure/azure-sdk-for-net/issues/48464))
1011

1112
### Other Changes
1213
- Marked `UsernamePasswordCredential` as obsolete because Resource Owner Password Credentials (ROPC) token grant flow is incompatible with multifactor authentication (MFA), which Microsoft Entra ID requires for all tenants. See https://aka.ms/azsdk/identity/mfa for details about MFA enforcement and migration guidance.

sdk/identity/Azure.Identity/src/Credentials/VisualStudioCredential.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public class VisualStudioCredential : TokenCredential
2626
private static readonly string TokenProviderFilePath = Path.Combine(".IdentityService", "AzureServiceAuth", "tokenprovider.json");
2727
private const string ResourceArgumentName = "--resource";
2828
private const string TenantArgumentName = "--tenant";
29+
private string[] UnavailableErrorStrings => new[] { "TS005" };
2930

3031
private readonly CredentialPipeline _pipeline;
3132
internal string TenantId { get; }
@@ -119,7 +120,16 @@ private async ValueTask<AccessToken> GetTokenImplAsync(TokenRequestContext reque
119120
}
120121
catch (Exception e)
121122
{
122-
throw scope.FailWrapAndThrow(e, isCredentialUnavailable: _isChainedCredential);
123+
bool containsUnavailableError = false;
124+
foreach (string errorString in UnavailableErrorStrings)
125+
{
126+
if (e.Message.Contains(errorString))
127+
{
128+
containsUnavailableError = true;
129+
break;
130+
}
131+
}
132+
throw scope.FailWrapAndThrow(e, isCredentialUnavailable: _isChainedCredential || containsUnavailableError);
123133
}
124134
}
125135

sdk/identity/Azure.Identity/tests/VisualStudioCredentialTests.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,18 @@ public void OperationCanceledException_throws_CredentialUnavailableException_Whe
335335
var fileSystem = CredentialTestHelpers.CreateFileSystemForVisualStudio();
336336
var credential = InstrumentClient(new VisualStudioCredential(default, default, fileSystem, new TestProcessService(testProcess), new VisualStudioCredentialOptions { IsChainedCredential = true }));
337337

338+
Assert.ThrowsAsync<CredentialUnavailableException>(
339+
async () => await credential.GetTokenAsync(new TokenRequestContext(new[] { "https://vault.azure.net/" }), CancellationToken.None));
340+
}
341+
342+
[Test]
343+
[TestCase("TS003: Error, TS005: No accounts found. Please go to Tools->Options->Azure Services Authentication, and add an account to be authenticated to Azure services during development.")]
344+
public void GeneralExceptions_With_CertainErrors_throws_CredentialUnavailableException(string exceptionMessage)
345+
{
346+
var testProcess = new TestProcess() { ExceptionOnStartHandler = p => throw new InvalidOperationException(exceptionMessage) };
347+
var fileSystem = CredentialTestHelpers.CreateFileSystemForVisualStudio();
348+
var credential = InstrumentClient(new VisualStudioCredential(default, default, fileSystem, new TestProcessService(testProcess), new VisualStudioCredentialOptions { IsChainedCredential = false }));
349+
338350
Assert.ThrowsAsync<CredentialUnavailableException>(
339351
async () => await credential.GetTokenAsync(new TokenRequestContext(new[] { "https://vault.azure.net/" }), CancellationToken.None));
340352
}

0 commit comments

Comments
 (0)