LWAAuthenticationProvider.cpp 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <AzCore/std/smart_ptr/make_shared.h>
  9. #include <Authentication/LWAAuthenticationProvider.h>
  10. #include <Authentication/AuthenticationProviderBus.h>
  11. #include <Authentication/OAuthConstants.h>
  12. #include <HttpRequestor/HttpRequestorBus.h>
  13. #include <HttpRequestor/HttpTypes.h>
  14. #include <aws/core/http/HttpResponse.h>
  15. namespace AWSClientAuth
  16. {
  17. constexpr char LwaSettingsPath[] = "/AWS/LoginWithAmazon";
  18. constexpr char LwaVerificationUrlResponseKey[] = "verification_uri";
  19. LWAAuthenticationProvider::LWAAuthenticationProvider()
  20. {
  21. m_settings = AZStd::make_unique<LWAProviderSetting>();
  22. }
  23. LWAAuthenticationProvider::~LWAAuthenticationProvider()
  24. {
  25. m_settings.reset();
  26. }
  27. bool LWAAuthenticationProvider::Initialize()
  28. {
  29. AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get();
  30. if (!settingsRegistry)
  31. {
  32. AZ_Warning("AWSCognitoAuthenticationProvider", false, "Failed to load the setting registry");
  33. return false;
  34. }
  35. if (!settingsRegistry->GetObject(m_settings.get(), azrtti_typeid(m_settings.get()), LwaSettingsPath))
  36. {
  37. AZ_Warning("AWSCognitoAuthenticationProvider", false, "Failed to get login with Amazon settings object for path %s", LwaSettingsPath);
  38. return false;
  39. }
  40. return true;
  41. }
  42. void LWAAuthenticationProvider::PasswordGrantSingleFactorSignInAsync(const AZStd::string& username, const AZStd::string& password)
  43. {
  44. AZ_UNUSED(username);
  45. AZ_UNUSED(password);
  46. AZ_Assert(false, "Not supported");
  47. }
  48. void LWAAuthenticationProvider::PasswordGrantMultiFactorSignInAsync(const AZStd::string& username, const AZStd::string& password)
  49. {
  50. AZ_UNUSED(username);
  51. AZ_UNUSED(password);
  52. AZ_Assert(false, "Not supported");
  53. }
  54. void LWAAuthenticationProvider::PasswordGrantMultiFactorConfirmSignInAsync(const AZStd::string& username, const AZStd::string& confirmationCode)
  55. {
  56. AZ_UNUSED(username);
  57. AZ_UNUSED(confirmationCode);
  58. AZ_Assert(false, "Not supported");
  59. }
  60. // Call LWA authentication provider device code end point.
  61. // Refer https://developer.amazon.com/docs/login-with-amazon/retrieve-code-other-platforms-cbl-docs.html.
  62. void LWAAuthenticationProvider::DeviceCodeGrantSignInAsync()
  63. {
  64. // Set headers and body for device sign in http requests.
  65. AZStd::string body = AZStd::string::format("%s=%s&%s=%s&%s=%s", OAuthResponseTypeBodyKey, m_settings->m_responseType.c_str()
  66. , OAuthClientIdBodyKey, m_settings->m_appClientId.c_str(), OAuthScopeBodyKey, OAuthScopeBodyValue);
  67. AZStd::map<AZStd::string, AZStd::string> headers;
  68. headers[OAuthContentTypeHeaderKey] = OAuthContentTypeHeaderValue;
  69. headers[OAuthContentLengthHeaderKey] = AZStd::to_string(body.length());
  70. HttpRequestor::HttpRequestorRequestBus::Broadcast(&HttpRequestor::HttpRequestorRequests::AddRequestWithHeadersAndBody, m_settings->m_oAuthCodeURL
  71. , Aws::Http::HttpMethod::HTTP_POST, headers, body
  72. , [this](const Aws::Utils::Json::JsonView& jsonView, Aws::Http::HttpResponseCode responseCode)
  73. {
  74. if (responseCode == Aws::Http::HttpResponseCode::OK)
  75. {
  76. m_cachedUserCode = jsonView.GetString(OAuthUserCodeResponseKey).c_str();
  77. m_cachedDeviceCode = jsonView.GetString(OAuthDeviceCodeBodyKey).c_str();
  78. AuthenticationProviderNotificationBus::Broadcast(&AuthenticationProviderNotifications::OnDeviceCodeGrantSignInSuccess
  79. , jsonView.GetString(OAuthUserCodeResponseKey).c_str()
  80. , jsonView.GetString(LwaVerificationUrlResponseKey).c_str()
  81. , jsonView.GetInteger(OAuthExpiresInResponseKey));
  82. }
  83. else
  84. {
  85. AuthenticationProviderNotificationBus::Broadcast(&AuthenticationProviderNotifications::OnDeviceCodeGrantSignInFail
  86. , jsonView.GetString(OAuthErrorResponseKey).c_str());
  87. }
  88. }
  89. );
  90. }
  91. // Call LWA authentication provider OAuth tokens endpoint
  92. // Refer https://developer.amazon.com/docs/login-with-amazon/retrieve-token-other-platforms-cbl-docs.html
  93. void LWAAuthenticationProvider::DeviceCodeGrantConfirmSignInAsync()
  94. {
  95. // Set headers and body for device confirm sign in http requests.
  96. AZStd::string body = AZStd::string::format("%s=%s&%s=%s&%s=%s", OAuthUserCodeResponseKey, m_cachedUserCode.c_str()
  97. , OAuthGrantTypeBodyKey, m_settings->m_grantType.c_str(), OAuthDeviceCodeBodyKey, m_cachedDeviceCode.c_str());
  98. AZStd::map<AZStd::string, AZStd::string> headers;
  99. headers[OAuthContentTypeHeaderKey] = OAuthContentTypeHeaderValue;
  100. headers[OAuthContentLengthHeaderKey] = AZStd::to_string(body.length());
  101. HttpRequestor::HttpRequestorRequestBus::Broadcast(&HttpRequestor::HttpRequestorRequests::AddRequestWithHeadersAndBody, m_settings->m_oAuthTokensURL
  102. , Aws::Http::HttpMethod::HTTP_POST, headers, body
  103. , [this](const Aws::Utils::Json::JsonView& jsonView, Aws::Http::HttpResponseCode responseCode)
  104. {
  105. if (responseCode == Aws::Http::HttpResponseCode::OK)
  106. {
  107. // Id and access token are the same.
  108. UpdateTokens(jsonView);
  109. AuthenticationProviderNotificationBus::Broadcast(&AuthenticationProviderNotifications::OnDeviceCodeGrantConfirmSignInSuccess,
  110. AuthenticationTokens(jsonView.GetString(OAuthAccessTokenResponseKey).c_str(),
  111. jsonView.GetString(OAuthRefreshTokenResponseKey).c_str(), jsonView.GetString(OAuthAccessTokenResponseKey).c_str(),
  112. ProviderNameEnum::LoginWithAmazon, jsonView.GetInteger(OAuthExpiresInResponseKey)));
  113. m_cachedUserCode = "";
  114. m_cachedDeviceCode = "";
  115. }
  116. else
  117. {
  118. AuthenticationProviderNotificationBus::Broadcast(&AuthenticationProviderNotifications::OnDeviceCodeGrantConfirmSignInFail
  119. , jsonView.GetString("error").c_str());
  120. }
  121. }
  122. );
  123. }
  124. void LWAAuthenticationProvider::RefreshTokensAsync()
  125. {
  126. // Set headers and body for device confirm sign in http requests.
  127. AZStd::string body = AZStd::string::format("%s=%s&%s=%s&%s=%s", OAuthClientIdBodyKey, m_settings->m_appClientId.c_str(), OAuthGrantTypeBodyKey,
  128. OAuthRefreshTokenBodyValue, OAuthRefreshTokenBodyKey, m_authenticationTokens.GetRefreshToken().c_str());
  129. AZStd::map<AZStd::string, AZStd::string> headers;
  130. headers[OAuthContentTypeHeaderKey] = OAuthContentTypeHeaderValue;
  131. headers[OAuthContentLengthHeaderKey] = AZStd::to_string(body.length());
  132. HttpRequestor::HttpRequestorRequestBus::Broadcast(&HttpRequestor::HttpRequestorRequests::AddRequestWithHeadersAndBody, m_settings->m_oAuthTokensURL
  133. , Aws::Http::HttpMethod::HTTP_POST, headers, body
  134. , [this](const Aws::Utils::Json::JsonView& jsonView, Aws::Http::HttpResponseCode responseCode)
  135. {
  136. if (responseCode == Aws::Http::HttpResponseCode::OK)
  137. {
  138. // Id and access token are the same.
  139. UpdateTokens(jsonView);
  140. AuthenticationProviderNotificationBus::Broadcast(&AuthenticationProviderNotifications::OnRefreshTokensSuccess,
  141. AuthenticationTokens(jsonView.GetString(OAuthAccessTokenResponseKey).c_str(),
  142. jsonView.GetString(OAuthRefreshTokenResponseKey).c_str(), jsonView.GetString(OAuthAccessTokenResponseKey).c_str(),
  143. ProviderNameEnum::LoginWithAmazon, jsonView.GetInteger(OAuthExpiresInResponseKey)));
  144. }
  145. else
  146. {
  147. AuthenticationProviderNotificationBus::Broadcast(&AuthenticationProviderNotifications::OnRefreshTokensFail
  148. , jsonView.GetString("error").c_str());
  149. }
  150. }
  151. );
  152. }
  153. void LWAAuthenticationProvider::UpdateTokens(const Aws::Utils::Json::JsonView& jsonView)
  154. {
  155. // Storing authentication tokens in memory can be a security concern. The access token and id token are not actually in use by
  156. // the authentication provider and shouldn't be stored in the member variable.
  157. m_authenticationTokens = AuthenticationTokens("", jsonView.GetString(OAuthRefreshTokenResponseKey).c_str(),
  158. "", ProviderNameEnum::LoginWithAmazon
  159. , jsonView.GetInteger(OAuthExpiresInResponseKey));
  160. }
  161. } // namespace AWSClientAuth