AWSCognitoAuthenticationProvider.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  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/weak_ptr.h>
  9. #include <AzCore/Jobs/JobFunction.h>
  10. #include <Authentication/AWSCognitoAuthenticationProvider.h>
  11. #include <Authentication/AuthenticationProviderTypes.h>
  12. #include <Authentication/AuthenticationProviderBus.h>
  13. #include <AWSClientAuthBus.h>
  14. #include <AWSCoreBus.h>
  15. #include <ResourceMapping/AWSResourceMappingBus.h>
  16. #include <AWSClientAuthResourceMappingConstants.h>
  17. #include <aws/cognito-idp/model/InitiateAuthRequest.h>
  18. #include <aws/cognito-idp/model/InitiateAuthResult.h>
  19. #include <aws/cognito-idp/model/RespondToAuthChallengeRequest.h>
  20. #include <aws/cognito-idp/model/RespondToAuthChallengeResult.h>
  21. #include <aws/cognito-idp/CognitoIdentityProviderClient.h>
  22. #include <aws/cognito-idp/CognitoIdentityProviderErrors.h>
  23. namespace AWSClientAuth
  24. {
  25. constexpr char CognitoUsernameKey[] = "USERNAME";
  26. constexpr char CognitoPasswordKey[] = "PASSWORD";
  27. constexpr char CognitoRefreshTokenAuthParamKey[] = "REFRESH_TOKEN";
  28. constexpr char CognitoSmsMfaCodeKey[] = "SMS_MFA_CODE";
  29. bool AWSCognitoAuthenticationProvider::Initialize()
  30. {
  31. AWSCore::AWSResourceMappingRequestBus::BroadcastResult(
  32. m_cognitoAppClientId, &AWSCore::AWSResourceMappingRequests::GetResourceNameId, CognitoAppClientIdResourceMappingKey);
  33. AZ_Warning("AWSCognitoAuthenticationProvider", !m_cognitoAppClientId.empty(), "Missing Cognito App Client Id from resource mappings. Calls to Cognito will fail.");
  34. return !m_cognitoAppClientId.empty();
  35. }
  36. void AWSCognitoAuthenticationProvider::PasswordGrantSingleFactorSignInAsync(const AZStd::string& username, const AZStd::string& password)
  37. {
  38. InitiateAuthInternalAsync(username, password, [this](Aws::CognitoIdentityProvider::Model::InitiateAuthOutcome initiateAuthOutcome)
  39. {
  40. if (initiateAuthOutcome.IsSuccess())
  41. {
  42. Aws::CognitoIdentityProvider::Model::InitiateAuthResult initiateAuthResult{ initiateAuthOutcome.GetResult() };
  43. if (initiateAuthResult.GetChallengeName() == Aws::CognitoIdentityProvider::Model::ChallengeNameType::NOT_SET)
  44. {
  45. Aws::CognitoIdentityProvider::Model::AuthenticationResultType authenticationResult = initiateAuthResult.GetAuthenticationResult();
  46. UpdateTokens(authenticationResult);
  47. AuthenticationProviderNotificationBus::Broadcast(&AuthenticationProviderNotifications::OnPasswordGrantSingleFactorSignInSuccess,
  48. AuthenticationTokens(authenticationResult.GetAccessToken().c_str(),
  49. authenticationResult.GetRefreshToken().c_str(), authenticationResult.GetIdToken().c_str(),
  50. ProviderNameEnum::AWSCognitoIDP, authenticationResult.GetExpiresIn()));
  51. }
  52. else
  53. {
  54. AuthenticationProviderNotificationBus::Broadcast(&AuthenticationProviderNotifications::OnPasswordGrantSingleFactorSignInFail
  55. , AZStd::string::format("Unexpected Challenge type: %s"
  56. , Aws::CognitoIdentityProvider::Model::ChallengeNameTypeMapper::GetNameForChallengeNameType(initiateAuthResult.GetChallengeName()).c_str()));
  57. }
  58. }
  59. else
  60. {
  61. Aws::Client::AWSError<Aws::CognitoIdentityProvider::CognitoIdentityProviderErrors> error = initiateAuthOutcome.GetError();
  62. AuthenticationProviderNotificationBus::Broadcast(&AuthenticationProviderNotifications::OnPasswordGrantSingleFactorSignInFail, error.GetMessage().c_str());
  63. }
  64. });
  65. }
  66. void AWSCognitoAuthenticationProvider::PasswordGrantMultiFactorSignInAsync(const AZStd::string& username, const AZStd::string& password)
  67. {
  68. InitiateAuthInternalAsync(username, password, [this](Aws::CognitoIdentityProvider::Model::InitiateAuthOutcome initiateAuthOutcome)
  69. {
  70. if (initiateAuthOutcome.IsSuccess())
  71. {
  72. Aws::CognitoIdentityProvider::Model::InitiateAuthResult initiateAuthResult{ initiateAuthOutcome.GetResult() };
  73. if (initiateAuthResult.GetChallengeName() == Aws::CognitoIdentityProvider::Model::ChallengeNameType::SMS_MFA)
  74. {
  75. Aws::CognitoIdentityProvider::Model::AuthenticationResultType authenticationResult = initiateAuthResult.GetAuthenticationResult();
  76. // Call on sign in success for MFA
  77. AuthenticationProviderNotificationBus::Broadcast(&AuthenticationProviderNotifications::OnPasswordGrantMultiFactorSignInSuccess);
  78. m_session = initiateAuthResult.GetSession().c_str();
  79. }
  80. else
  81. {
  82. AuthenticationProviderNotificationBus::Broadcast(&AuthenticationProviderNotifications::OnPasswordGrantMultiFactorSignInFail
  83. , AZStd::string::format("Unexpected Challenge type: %s"
  84. , Aws::CognitoIdentityProvider::Model::ChallengeNameTypeMapper::GetNameForChallengeNameType(initiateAuthResult.GetChallengeName()).c_str()));
  85. }
  86. }
  87. else
  88. {
  89. Aws::Client::AWSError<Aws::CognitoIdentityProvider::CognitoIdentityProviderErrors> error = initiateAuthOutcome.GetError();
  90. AuthenticationProviderNotificationBus::Broadcast(&AuthenticationProviderNotifications::OnPasswordGrantMultiFactorSignInFail, error.GetMessage().c_str());
  91. }
  92. });
  93. }
  94. // Call RespondToAuthChallenge for Cognito authentication flow.
  95. // Refer https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-authentication-flow.html.
  96. void AWSCognitoAuthenticationProvider::PasswordGrantMultiFactorConfirmSignInAsync(const AZStd::string& username, const AZStd::string& confirmationCode)
  97. {
  98. std::shared_ptr<Aws::CognitoIdentityProvider::CognitoIdentityProviderClient> cognitoIdentityProviderClient =
  99. AZ::Interface<IAWSClientAuthRequests>::Get()->GetCognitoIDPClient();
  100. AZ::JobContext* jobContext = nullptr;
  101. AWSCore::AWSCoreRequestBus::BroadcastResult(jobContext, &AWSCore::AWSCoreRequests::GetDefaultJobContext);
  102. AZ::Job* confirmSignInJob = AZ::CreateJobFunction([this, cognitoIdentityProviderClient, confirmationCode, username]()
  103. {
  104. // Set Request parameters for SMS Multi factor authentication.
  105. // Note: Email MFA is no longer supported by Cognito, use SMS as MFA
  106. Aws::CognitoIdentityProvider::Model::RespondToAuthChallengeRequest respondToAuthChallengeRequest;
  107. respondToAuthChallengeRequest.SetClientId(m_cognitoAppClientId.c_str());
  108. respondToAuthChallengeRequest.AddChallengeResponses(CognitoSmsMfaCodeKey, confirmationCode.c_str());
  109. respondToAuthChallengeRequest.AddChallengeResponses(CognitoUsernameKey, username.c_str());
  110. respondToAuthChallengeRequest.SetChallengeName(Aws::CognitoIdentityProvider::Model::ChallengeNameType::SMS_MFA);
  111. respondToAuthChallengeRequest.SetSession(m_session.c_str());
  112. Aws::CognitoIdentityProvider::Model::RespondToAuthChallengeOutcome respondToAuthChallengeOutcome{ cognitoIdentityProviderClient->RespondToAuthChallenge(respondToAuthChallengeRequest) };
  113. if (respondToAuthChallengeOutcome.IsSuccess())
  114. {
  115. Aws::CognitoIdentityProvider::Model::RespondToAuthChallengeResult respondToAuthChallengeResult{ respondToAuthChallengeOutcome.GetResult() };
  116. if (respondToAuthChallengeResult.GetChallengeName() == Aws::CognitoIdentityProvider::Model::ChallengeNameType::NOT_SET)
  117. {
  118. Aws::CognitoIdentityProvider::Model::AuthenticationResultType authenticationResult = respondToAuthChallengeResult.GetAuthenticationResult();
  119. UpdateTokens(authenticationResult);
  120. AuthenticationProviderNotificationBus::Broadcast(&AuthenticationProviderNotifications::OnPasswordGrantMultiFactorConfirmSignInSuccess,
  121. AuthenticationTokens(authenticationResult.GetAccessToken().c_str(),
  122. authenticationResult.GetRefreshToken().c_str(), authenticationResult.GetIdToken().c_str(),
  123. ProviderNameEnum::AWSCognitoIDP, authenticationResult.GetExpiresIn()));
  124. }
  125. }
  126. else
  127. {
  128. Aws::Client::AWSError<Aws::CognitoIdentityProvider::CognitoIdentityProviderErrors> error = respondToAuthChallengeOutcome.GetError();
  129. AuthenticationProviderNotificationBus::Broadcast(&AuthenticationProviderNotifications::OnPasswordGrantMultiFactorConfirmSignInFail, error.GetMessage().c_str());
  130. }
  131. }, true, jobContext);
  132. confirmSignInJob->Start();
  133. }
  134. void AWSCognitoAuthenticationProvider::DeviceCodeGrantSignInAsync()
  135. {
  136. AZ_Assert(false, "Not supported");
  137. }
  138. void AWSCognitoAuthenticationProvider::DeviceCodeGrantConfirmSignInAsync()
  139. {
  140. AZ_Assert(false, "Not supported");
  141. }
  142. void AWSCognitoAuthenticationProvider::RefreshTokensAsync()
  143. {
  144. std::shared_ptr<Aws::CognitoIdentityProvider::CognitoIdentityProviderClient> cognitoIdentityProviderClient =
  145. AZ::Interface<IAWSClientAuthRequests>::Get()->GetCognitoIDPClient();
  146. AZ::JobContext* jobContext = nullptr;
  147. AWSCore::AWSCoreRequestBus::BroadcastResult(jobContext, &AWSCore::AWSCoreRequests::GetDefaultJobContext);
  148. AZ::Job* initiateAuthJob = AZ::CreateJobFunction([this, cognitoIdentityProviderClient]()
  149. {
  150. // Set Request parameters.
  151. Aws::CognitoIdentityProvider::Model::InitiateAuthRequest initiateAuthRequest;
  152. initiateAuthRequest.SetClientId(m_cognitoAppClientId.c_str());
  153. initiateAuthRequest.SetAuthFlow(Aws::CognitoIdentityProvider::Model::AuthFlowType::REFRESH_TOKEN_AUTH);
  154. // Set username and password for Password grant/ Initiate Auth flow.
  155. Aws::Map<Aws::String, Aws::String> authParameters
  156. {
  157. {CognitoRefreshTokenAuthParamKey, GetAuthenticationTokens().GetRefreshToken().c_str()}
  158. };
  159. initiateAuthRequest.SetAuthParameters(authParameters);
  160. Aws::CognitoIdentityProvider::Model::InitiateAuthOutcome initiateAuthOutcome{ cognitoIdentityProviderClient->InitiateAuth(initiateAuthRequest) };
  161. if (initiateAuthOutcome.IsSuccess())
  162. {
  163. Aws::CognitoIdentityProvider::Model::InitiateAuthResult initiateAuthResult{ initiateAuthOutcome.GetResult() };
  164. if (initiateAuthResult.GetChallengeName() == Aws::CognitoIdentityProvider::Model::ChallengeNameType::NOT_SET)
  165. {
  166. Aws::CognitoIdentityProvider::Model::AuthenticationResultType authenticationResult = initiateAuthResult.GetAuthenticationResult();
  167. UpdateTokens(authenticationResult);
  168. AuthenticationProviderNotificationBus::Broadcast(&AuthenticationProviderNotifications::OnRefreshTokensSuccess,
  169. AuthenticationTokens(authenticationResult.GetAccessToken().c_str(),
  170. authenticationResult.GetRefreshToken().c_str(), authenticationResult.GetIdToken().c_str(),
  171. ProviderNameEnum::AWSCognitoIDP, authenticationResult.GetExpiresIn()));
  172. }
  173. else
  174. {
  175. AuthenticationProviderNotificationBus::Broadcast(&AuthenticationProviderNotifications::OnRefreshTokensFail
  176. , AZStd::string::format("Unexpected Challenge type: %s"
  177. , Aws::CognitoIdentityProvider::Model::ChallengeNameTypeMapper::GetNameForChallengeNameType(initiateAuthResult.GetChallengeName()).c_str()));
  178. }
  179. }
  180. else
  181. {
  182. Aws::Client::AWSError<Aws::CognitoIdentityProvider::CognitoIdentityProviderErrors> error = initiateAuthOutcome.GetError();
  183. AuthenticationProviderNotificationBus::Broadcast(&AuthenticationProviderNotifications::OnRefreshTokensFail, error.GetMessage().c_str());
  184. }
  185. }, true, jobContext);
  186. initiateAuthJob->Start();
  187. }
  188. // Call InitiateAuth for Cognito authentication flow.
  189. // Refer https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-authentication-flow.html.
  190. void AWSCognitoAuthenticationProvider::InitiateAuthInternalAsync(const AZStd::string& username, const AZStd::string& password
  191. , AZStd::function<void(Aws::CognitoIdentityProvider::Model::InitiateAuthOutcome outcome)> outcomeCallback)
  192. {
  193. std::shared_ptr<Aws::CognitoIdentityProvider::CognitoIdentityProviderClient> cognitoIdentityProviderClient =
  194. AZ::Interface<IAWSClientAuthRequests>::Get()->GetCognitoIDPClient();
  195. AZ::JobContext* jobContext = nullptr;
  196. AWSCore::AWSCoreRequestBus::BroadcastResult(jobContext, &AWSCore::AWSCoreRequests::GetDefaultJobContext);
  197. AZ::Job* initiateAuthJob = AZ::CreateJobFunction([this, cognitoIdentityProviderClient, username, password, outcomeCallback]()
  198. {
  199. // Set Request parameters.
  200. Aws::CognitoIdentityProvider::Model::InitiateAuthRequest initiateAuthRequest;
  201. initiateAuthRequest.SetClientId(m_cognitoAppClientId.c_str());
  202. initiateAuthRequest.SetAuthFlow(Aws::CognitoIdentityProvider::Model::AuthFlowType::USER_PASSWORD_AUTH);
  203. // Set username and password for Password grant/ Initiate Auth flow.
  204. Aws::Map<Aws::String, Aws::String> authParameters
  205. {
  206. {CognitoUsernameKey, username.c_str()},
  207. {CognitoPasswordKey, password.c_str()}
  208. };
  209. initiateAuthRequest.SetAuthParameters(authParameters);
  210. Aws::CognitoIdentityProvider::Model::InitiateAuthOutcome initiateAuthOutcome{ cognitoIdentityProviderClient->InitiateAuth(initiateAuthRequest) };
  211. outcomeCallback(initiateAuthOutcome);
  212. }, true, jobContext);
  213. initiateAuthJob->Start();
  214. }
  215. void AWSCognitoAuthenticationProvider::UpdateTokens(const Aws::CognitoIdentityProvider::Model::AuthenticationResultType& authenticationResult)
  216. {
  217. // Storing authentication tokens in memory can be a security concern. The access token and id token are not actually in use by
  218. // the authentication provider and shouldn't be stored in the member variable.
  219. m_authenticationTokens = AuthenticationTokens("", authenticationResult.GetRefreshToken().c_str(),
  220. "", ProviderNameEnum::AWSCognitoIDP,
  221. authenticationResult.GetExpiresIn());
  222. }
  223. } // namespace AWSClientAuth