Skip to content

Commit

Permalink
DAC developer credentials fail with CredentialUnavailableException (#…
Browse files Browse the repository at this point in the history
christothes authored Jun 13, 2023
1 parent e62e1d6 commit 3df3081
Showing 10 changed files with 37 additions and 19 deletions.
1 change: 1 addition & 0 deletions sdk/identity/Azure.Identity/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@
### Bugs Fixed

### Other Changes
- All developer credentials in the `DefaultAzureCredential` credential chain will fall through to the next credential in the chain on any failure. Previously, some exceptions would throw `AuthenticationFailedException`, which stops further progress in the chain.

## 1.9.0 (2023-05-09)

8 changes: 5 additions & 3 deletions sdk/identity/Azure.Identity/src/CredentialDiagnosticScope.cs
Original file line number Diff line number Diff line change
@@ -36,7 +36,7 @@ public AccessToken Succeeded(AccessToken token)
return token;
}

public Exception FailWrapAndThrow(Exception ex, string additionalMessage = null)
public Exception FailWrapAndThrow(Exception ex, string additionalMessage = null, bool isCredentialUnavailable = false)
{
var wrapped = TryWrapException(ref ex, additionalMessage);
RegisterFailed(ex);
@@ -55,7 +55,7 @@ private void RegisterFailed(Exception ex)
_scopeHandler.Fail(_name, _scope, ex);
}

private bool TryWrapException(ref Exception exception, string additionalMessageText = null)
private bool TryWrapException(ref Exception exception, string additionalMessageText = null, bool isCredentialUnavailable = false)
{
if (exception is OperationCanceledException || exception is AuthenticationFailedException)
{
@@ -76,7 +76,9 @@ private bool TryWrapException(ref Exception exception, string additionalMessageT
{
exceptionMessage = exceptionMessage + $"\n{additionalMessageText}";
}
exception = new AuthenticationFailedException(exceptionMessage, exception);
exception = isCredentialUnavailable ?
new CredentialUnavailableException(exceptionMessage, exception) :
new AuthenticationFailedException(exceptionMessage, exception);
return true;
}

Original file line number Diff line number Diff line change
@@ -132,7 +132,7 @@ private async ValueTask<AccessToken> RequestCliAccessTokenAsync(bool async, Toke
}
catch (OperationCanceledException) when (!cancellationToken.IsCancellationRequested)
{
throw new AuthenticationFailedException(AzureCliTimeoutError);
throw new CredentialUnavailableException(AzureCliTimeoutError);
}
catch (InvalidOperationException exception)
{
@@ -163,7 +163,7 @@ private async ValueTask<AccessToken> RequestCliAccessTokenAsync(bool async, Toke
throw new CredentialUnavailableException(InteractiveLoginRequired);
}

throw new AuthenticationFailedException($"{AzureCliFailedError} {Troubleshoot} {exception.Message}");
throw new CredentialUnavailableException($"{AzureCliFailedError} {Troubleshoot} {exception.Message}");
}

AccessToken token = DeserializeOutput(output);
Original file line number Diff line number Diff line change
@@ -123,7 +123,7 @@ private async ValueTask<AccessToken> RequestCliAccessTokenAsync(bool async, Toke
}
catch (OperationCanceledException) when (!cancellationToken.IsCancellationRequested)
{
throw new AuthenticationFailedException(AzdCliTimeoutError);
throw new CredentialUnavailableException(AzdCliTimeoutError);
}
catch (InvalidOperationException exception)
{
@@ -153,7 +153,7 @@ private async ValueTask<AccessToken> RequestCliAccessTokenAsync(bool async, Toke
throw new CredentialUnavailableException(InteractiveLoginRequired);
}

throw new AuthenticationFailedException($"{AzdCliFailedError} {Troubleshoot} {exception.Message}");
throw new CredentialUnavailableException($"{AzdCliFailedError} {Troubleshoot} {exception.Message}");
}

AccessToken token = DeserializeOutput(output);
Original file line number Diff line number Diff line change
@@ -124,12 +124,12 @@ private async ValueTask<AccessToken> GetTokenImplAsync(bool async, TokenRequestC
}
catch (Exception e)
{
throw scope.FailWrapAndThrow(e);
throw scope.FailWrapAndThrow(e, isCredentialUnavailable: true);
}
}
catch (Exception e)
{
throw scope.FailWrapAndThrow(e);
throw scope.FailWrapAndThrow(e, isCredentialUnavailable: true);
}
}

@@ -162,7 +162,7 @@ private async ValueTask<AccessToken> RequestAzurePowerShellAccessTokenAsync(bool
catch (InvalidOperationException exception)
{
CheckForErrors(exception.Message);
throw new AuthenticationFailedException($"{AzurePowerShellFailedError} {exception.Message}");
throw new CredentialUnavailableException($"{AzurePowerShellFailedError} {exception.Message}");
}
return DeserializeOutput(output);
}
Original file line number Diff line number Diff line change
@@ -102,10 +102,14 @@ private async ValueTask<AccessToken> GetTokenImplAsync(TokenRequestContext reque

return scope.Succeeded(accessToken);
}
catch (Exception e)
catch (CredentialUnavailableException e)
{
throw scope.FailWrapAndThrow(e);
}
catch (Exception e)
{
throw scope.FailWrapAndThrow(e, isCredentialUnavailable: true);
}
}

private static string GetTokenProviderPath()
6 changes: 3 additions & 3 deletions sdk/identity/Azure.Identity/tests/AzureCliCredentialTests.cs
Original file line number Diff line number Diff line change
@@ -103,8 +103,8 @@ public static IEnumerable<object[]> AzureCliExceptionScenarios()
yield return new object[] { AzureCliCredential.AzNotLogIn, AzureCliCredential.AzNotLogIn, typeof(CredentialUnavailableException) };
yield return new object[] { RefreshTokenExpiredError, AzureCliCredential.InteractiveLoginRequired, typeof(CredentialUnavailableException) };
yield return new object[] { AzureCliCredential.CLIInternalError, AzureCliCredential.InteractiveLoginRequired, typeof(CredentialUnavailableException) };
yield return new object[] { "random unknown exception", AzureCliCredential.AzureCliFailedError + " " + AzureCliCredential.Troubleshoot + " random unknown exception", typeof(AuthenticationFailedException) };
yield return new object[] { "AADSTS12345: Some AAD error. To re-authenticate, please run: az login", AzureCliCredential.AzureCliFailedError + " " + AzureCliCredential.Troubleshoot + " AADSTS12345: Some AAD error. To re-authenticate, please run: az login", typeof(AuthenticationFailedException) };
yield return new object[] { "random unknown exception", AzureCliCredential.AzureCliFailedError + " " + AzureCliCredential.Troubleshoot + " random unknown exception", typeof(CredentialUnavailableException) };
yield return new object[] { "AADSTS12345: Some AAD error. To re-authenticate, please run: az login", AzureCliCredential.AzureCliFailedError + " " + AzureCliCredential.Troubleshoot + " AADSTS12345: Some AAD error. To re-authenticate, please run: az login", typeof(CredentialUnavailableException) };
}

[Test]
@@ -135,7 +135,7 @@ public void ConfigureCliProcessTimeout_ProcessTimeout()
new AzureCliCredential(CredentialPipeline.GetInstance(null),
new TestProcessService(testProcess),
new AzureCliCredentialOptions() { ProcessTimeout = TimeSpan.Zero }));
var ex = Assert.ThrowsAsync<AuthenticationFailedException>(async () => await credential.GetTokenAsync(new TokenRequestContext(MockScopes.Default)));
var ex = Assert.ThrowsAsync<CredentialUnavailableException>(async () => await credential.GetTokenAsync(new TokenRequestContext(MockScopes.Default)));
Assert.AreEqual(AzureCliCredential.AzureCliTimeoutError, ex.Message);
}
}
Original file line number Diff line number Diff line change
@@ -90,8 +90,8 @@ public static IEnumerable<object[]> AzureDeveloperCliExceptionScenarios()
yield return new object[] { AzureDeveloperCliCredential.AzdNotLogIn, AzureDeveloperCliCredential.AzdNotLogIn, typeof(CredentialUnavailableException) };
yield return new object[] { RefreshTokenExpiredError, AzureDeveloperCliCredential.InteractiveLoginRequired, typeof(CredentialUnavailableException) };
yield return new object[] { AzureDeveloperCliCredential.AzdCLIInternalError, AzureDeveloperCliCredential.InteractiveLoginRequired, typeof(CredentialUnavailableException) };
yield return new object[] { "random unknown exception", AzureDeveloperCliCredential.AzdCliFailedError + " " + AzureDeveloperCliCredential.Troubleshoot + " random unknown exception", typeof(AuthenticationFailedException) };
yield return new object[] { "AADSTS12345: Some AAD error. To re-authenticate, please run: azd auth login", AzureDeveloperCliCredential.AzdCliFailedError + " " + AzureDeveloperCliCredential.Troubleshoot + " AADSTS12345: Some AAD error. To re-authenticate, please run: azd auth login", typeof(AuthenticationFailedException) };
yield return new object[] { "random unknown exception", AzureDeveloperCliCredential.AzdCliFailedError + " " + AzureDeveloperCliCredential.Troubleshoot + " random unknown exception", typeof(CredentialUnavailableException) };
yield return new object[] { "AADSTS12345: Some AAD error. To re-authenticate, please run: azd auth login", AzureDeveloperCliCredential.AzdCliFailedError + " " + AzureDeveloperCliCredential.Troubleshoot + " AADSTS12345: Some AAD error. To re-authenticate, please run: azd auth login", typeof(CredentialUnavailableException) };
}

[Test]
@@ -122,7 +122,7 @@ public void ConfigureCliProcessTimeout_ProcessTimeout()
new AzureDeveloperCliCredential(CredentialPipeline.GetInstance(null),
new TestProcessService(testProcess),
new AzureDeveloperCliCredentialOptions() { ProcessTimeout = TimeSpan.Zero }));
var ex = Assert.ThrowsAsync<AuthenticationFailedException>(async () => await credential.GetTokenAsync(new TokenRequestContext(MockScopes.Default)));
var ex = Assert.ThrowsAsync<CredentialUnavailableException>(async () => await credential.GetTokenAsync(new TokenRequestContext(MockScopes.Default)));
Assert.AreEqual(AzureDeveloperCliCredential.AzdCliTimeoutError, ex.Message);
}
}
Original file line number Diff line number Diff line change
@@ -92,10 +92,11 @@ private static IEnumerable<object[]> ErrorScenarios()
yield return new object[] { "Get-AzAccessToken: Run Connect-AzAccount to login.", AzurePowerShellCredential.AzurePowerShellNotLogInError, typeof(CredentialUnavailableException) };
yield return new object[] { "No accounts were found in the cache", AzurePowerShellCredential.AzurePowerShellNotLogInError, typeof(CredentialUnavailableException) };
yield return new object[] { "cannot retrieve access token", AzurePowerShellCredential.AzurePowerShellNotLogInError, typeof(CredentialUnavailableException) };
yield return new object[] { "Some random exception", AzurePowerShellCredential.AzurePowerShellFailedError + " Some random exception", typeof(CredentialUnavailableException) };
yield return new object[] {
"AADSTS500011: The resource principal named <RESOURCE> was not found in the tenant named",
AzurePowerShellCredential.AzurePowerShellFailedError + " AADSTS500011: The resource principal named <RESOURCE> was not found in the tenant named",
typeof(AuthenticationFailedException) };
typeof(CredentialUnavailableException) };
}

[Test]
@@ -220,7 +221,7 @@ public void AuthenticateWithAzurePowerShellCredential_AzurePowerShellUnknownErro
var testProcess = new TestProcess { Error = mockResult };
AzurePowerShellCredential credential = InstrumentClient(
new AzurePowerShellCredential(new AzurePowerShellCredentialOptions(), CredentialPipeline.GetInstance(null), new TestProcessService(testProcess)));
Assert.ThrowsAsync<AuthenticationFailedException>(async () => await credential.GetTokenAsync(new TokenRequestContext(MockScopes.Default)));
Assert.ThrowsAsync<CredentialUnavailableException>(async () => await credential.GetTokenAsync(new TokenRequestContext(MockScopes.Default)));
}

[Test]
10 changes: 10 additions & 0 deletions sdk/identity/Azure.Identity/tests/VisualStudioCredentialTests.cs
Original file line number Diff line number Diff line change
@@ -279,5 +279,15 @@ public void ConfigureVisualStudioProcessTimeout_ProcessTimeout()
var ex = Assert.ThrowsAsync<CredentialUnavailableException>(async () => await credential.GetTokenAsync(new TokenRequestContext(MockScopes.Default), CancellationToken.None));
Assert.True(ex.Message.Contains("has failed to get access token in 0 seconds."));
}

[Test]
public void GenericException_throws_CredentialUnavailableException()
{
var testProcess = new TestProcess() { ExceptionOnStartHandler = p => throw new Exception("Test exception") };
var fileSystem = CredentialTestHelpers.CreateFileSystemForVisualStudio();
var credential = InstrumentClient(new VisualStudioCredential(default, default, fileSystem, new TestProcessService(testProcess)));
Assert.ThrowsAsync<CredentialUnavailableException>(
async () => await credential.GetTokenAsync(new TokenRequestContext(new[] { "https://vault.azure.net/" }), CancellationToken.None));
}
}
}

0 comments on commit 3df3081

Please sign in to comment.