From 8880eb3d720f2547ac16d83b775cb47277df7fe9 Mon Sep 17 00:00:00 2001 From: Anton Kolesnyk Date: Thu, 23 Nov 2023 18:51:33 -0800 Subject: [PATCH] Include error details into Identity exceptions --- sdk/identity/azure-identity/CHANGELOG.md | 2 + .../src/token_credential_impl.cpp | 22 +++++----- .../test/ut/token_credential_impl_test.cpp | 43 +++++++++++++++++++ 3 files changed, 57 insertions(+), 10 deletions(-) diff --git a/sdk/identity/azure-identity/CHANGELOG.md b/sdk/identity/azure-identity/CHANGELOG.md index a024f17a03..3e90814293 100644 --- a/sdk/identity/azure-identity/CHANGELOG.md +++ b/sdk/identity/azure-identity/CHANGELOG.md @@ -10,6 +10,8 @@ ### Other Changes +- [[#5141]](https://github.com/Azure/azure-sdk-for-cpp/issues/5141) Added error response details to the Identity exceptions thrown when the authority host returns error response. + ## 1.6.0 (2023-11-10) ### Features Added diff --git a/sdk/identity/azure-identity/src/token_credential_impl.cpp b/sdk/identity/azure-identity/src/token_credential_impl.cpp index aeb8b1397d..474e4e5c0a 100644 --- a/sdk/identity/azure-identity/src/token_credential_impl.cpp +++ b/sdk/identity/azure-identity/src/token_credential_impl.cpp @@ -72,16 +72,13 @@ std::string TokenCredentialImpl::FormatScopes( if (scopesIter != scopesEnd) { - auto const scope = *scopesIter; - scopesStr += OptionalUrlEncode(scope, urlEncode); - } - - for (++scopesIter; scopesIter != scopesEnd; ++scopesIter) - { - auto const Separator = std::string(" "); // Element separator never gets URL-encoded + scopesStr += OptionalUrlEncode(*scopesIter, urlEncode); - auto const scope = *scopesIter; - scopesStr += Separator + OptionalUrlEncode(scope, urlEncode); + constexpr auto Separator = " "; // Element separator never gets URL-encoded + for (++scopesIter; scopesIter != scopesEnd; ++scopesIter) + { + scopesStr += Separator + OptionalUrlEncode(*scopesIter, urlEncode); + } } } @@ -117,11 +114,16 @@ AccessToken TokenCredentialImpl::GetToken( request = shouldRetry(statusCode, *response); if (request == nullptr) { + auto const bodyStream = response->ExtractBodyStream(); + auto const bodyVec = bodyStream ? bodyStream->ReadToEnd(context) : response->GetBody(); + auto const responseBody + = std::string(reinterpret_cast(bodyVec.data()), bodyVec.size()); + throw std::runtime_error( std::string("error response: ") + std::to_string( static_cast::type>(statusCode)) - + " " + response->GetReasonPhrase()); + + " " + response->GetReasonPhrase() + "\n\n" + responseBody); } response.reset(); diff --git a/sdk/identity/azure-identity/test/ut/token_credential_impl_test.cpp b/sdk/identity/azure-identity/test/ut/token_credential_impl_test.cpp index ab07a4c0ae..5ff80f5a70 100644 --- a/sdk/identity/azure-identity/test/ut/token_credential_impl_test.cpp +++ b/sdk/identity/azure-identity/test/ut/token_credential_impl_test.cpp @@ -710,6 +710,49 @@ TEST(TokenCredentialImpl, MaxValues) std::exception); } +TEST(TokenCredentialImpl, AuthErrorResponse) +{ + std::string const errorJson + = "{\"error\":\"invalid_request\"," + "\"error_description\":\"AADSTS90014: " + "The required field 'scope' is missing from the credential. " + "Ensure that you have all the necessary parameters for the login request. " + "Trace ID: 01234567-89ab-cdef-0123-456789abcdef " + "Correlation ID: fedcba98-7654-3210-0123-456789abcdef " + "Timestamp: 2023-11-30 00:51:37Z\"," + "\"error_codes\":[90014]," + "\"timestamp\":\"2023-11-30 00:51:37Z\"," + "\"trace_id\":\"01234567-89ab-cdef-0123-456789abcdef\"," + "\"correlation_id\":\"fedcba98-7654-3210-0123-456789abcdef\"," + "\"error_uri\":\"https://login.microsoftonline.com/error?code=90014\"}"; + + try + { + using Azure::Core::Http::HttpStatusCode; + std::vector const testScopes; + CredentialTestHelper::TokenRequestSimulationServerResponse testResponse; + testResponse.StatusCode = HttpStatusCode::BadRequest; + testResponse.Body = errorJson; + + static_cast(CredentialTestHelper::SimulateTokenRequest( + [](auto transport) { + TokenCredentialOptions options; + options.Transport.Transport = transport; + + return std::make_unique( + HttpMethod::Delete, Url("https://outlook.com/"), options); + }, + {testScopes}, + {testResponse})); + + EXPECT_TRUE(!"TokenCredentialImpl should throw given the response above."); + } + catch (AuthenticationException const& ex) + { + EXPECT_EQ(ex.what(), "GetToken(): error response: 400 Test\n\n" + errorJson); + } +} + TEST(TokenCredentialImpl, Diagnosability) { using Azure::Core::Diagnostics::Logger;