You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Gems/AWSClientAuth/Code/Source/Authentication/AWSCognitoAuthenticationPro...

244 lines
14 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/std/smart_ptr/weak_ptr.h>
#include <AzCore/Jobs/JobFunction.h>
#include <Authentication/AWSCognitoAuthenticationProvider.h>
#include <Authentication/AuthenticationProviderTypes.h>
#include <Authentication/AuthenticationProviderBus.h>
#include <AWSClientAuthBus.h>
#include <AWSCoreBus.h>
#include <ResourceMapping/AWSResourceMappingBus.h>
#include <AWSClientAuthResourceMappingConstants.h>
#include <aws/cognito-idp/model/InitiateAuthRequest.h>
#include <aws/cognito-idp/model/InitiateAuthResult.h>
#include <aws/cognito-idp/model/RespondToAuthChallengeRequest.h>
#include <aws/cognito-idp/model/RespondToAuthChallengeResult.h>
#include <aws/cognito-idp/CognitoIdentityProviderClient.h>
#include <aws/cognito-idp/CognitoIdentityProviderErrors.h>
namespace AWSClientAuth
{
constexpr char CognitoUsernameKey[] = "USERNAME";
constexpr char CognitoPasswordKey[] = "PASSWORD";
constexpr char CognitoRefreshTokenAuthParamKey[] = "REFRESH_TOKEN";
constexpr char CognitoSmsMfaCodeKey[] = "SMS_MFA_CODE";
bool AWSCognitoAuthenticationProvider::Initialize(AZStd::weak_ptr<AZ::SettingsRegistryInterface> settingsRegistry)
{
AZ_UNUSED(settingsRegistry);
AWSCore::AWSResourceMappingRequestBus::BroadcastResult(
m_cognitoAppClientId, &AWSCore::AWSResourceMappingRequests::GetResourceNameId, CognitoAppClientIdResourceMappingKey);
AZ_Warning("AWSCognitoAuthenticationProvider", !m_cognitoAppClientId.empty(), "Missing Cognito App Client Id from resource mappings. Calls to Cognito will fail.");
return !m_cognitoAppClientId.empty();
}
void AWSCognitoAuthenticationProvider::PasswordGrantSingleFactorSignInAsync(const AZStd::string& username, const AZStd::string& password)
{
InitiateAuthInternalAsync(username, password, [this](Aws::CognitoIdentityProvider::Model::InitiateAuthOutcome initiateAuthOutcome)
{
if (initiateAuthOutcome.IsSuccess())
{
Aws::CognitoIdentityProvider::Model::InitiateAuthResult initiateAuthResult{ initiateAuthOutcome.GetResult() };
if (initiateAuthResult.GetChallengeName() == Aws::CognitoIdentityProvider::Model::ChallengeNameType::NOT_SET)
{
Aws::CognitoIdentityProvider::Model::AuthenticationResultType authenticationResult = initiateAuthResult.GetAuthenticationResult();
UpdateTokens(authenticationResult);
AuthenticationProviderNotificationBus::Broadcast(&AuthenticationProviderNotifications::OnPasswordGrantSingleFactorSignInSuccess
, m_authenticationTokens);
}
else
{
AuthenticationProviderNotificationBus::Broadcast(&AuthenticationProviderNotifications::OnPasswordGrantSingleFactorSignInFail
, AZStd::string::format("Unexpected Challenge type: %s"
, Aws::CognitoIdentityProvider::Model::ChallengeNameTypeMapper::GetNameForChallengeNameType(initiateAuthResult.GetChallengeName()).c_str()));
}
}
else
{
Aws::Client::AWSError<Aws::CognitoIdentityProvider::CognitoIdentityProviderErrors> error = initiateAuthOutcome.GetError();
AuthenticationProviderNotificationBus::Broadcast(&AuthenticationProviderNotifications::OnPasswordGrantSingleFactorSignInFail, error.GetMessage().c_str());
}
});
}
void AWSCognitoAuthenticationProvider::PasswordGrantMultiFactorSignInAsync(const AZStd::string& username, const AZStd::string& password)
{
InitiateAuthInternalAsync(username, password, [this](Aws::CognitoIdentityProvider::Model::InitiateAuthOutcome initiateAuthOutcome)
{
if (initiateAuthOutcome.IsSuccess())
{
Aws::CognitoIdentityProvider::Model::InitiateAuthResult initiateAuthResult{ initiateAuthOutcome.GetResult() };
if (initiateAuthResult.GetChallengeName() == Aws::CognitoIdentityProvider::Model::ChallengeNameType::SMS_MFA)
{
Aws::CognitoIdentityProvider::Model::AuthenticationResultType authenticationResult = initiateAuthResult.GetAuthenticationResult();
// Call on sign in success for MFA
AuthenticationProviderNotificationBus::Broadcast(&AuthenticationProviderNotifications::OnPasswordGrantMultiFactorSignInSuccess);
m_session = initiateAuthResult.GetSession().c_str();
}
else
{
AuthenticationProviderNotificationBus::Broadcast(&AuthenticationProviderNotifications::OnPasswordGrantMultiFactorSignInFail
, AZStd::string::format("Unexpected Challenge type: %s"
, Aws::CognitoIdentityProvider::Model::ChallengeNameTypeMapper::GetNameForChallengeNameType(initiateAuthResult.GetChallengeName()).c_str()));
}
}
else
{
Aws::Client::AWSError<Aws::CognitoIdentityProvider::CognitoIdentityProviderErrors> error = initiateAuthOutcome.GetError();
AuthenticationProviderNotificationBus::Broadcast(&AuthenticationProviderNotifications::OnPasswordGrantMultiFactorSignInFail, error.GetMessage().c_str());
}
});
}
// Call RespondToAuthChallenge for Cognito authentication flow.
// Refer https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-authentication-flow.html.
void AWSCognitoAuthenticationProvider::PasswordGrantMultiFactorConfirmSignInAsync(const AZStd::string& username, const AZStd::string& confirmationCode)
{
std::shared_ptr<Aws::CognitoIdentityProvider::CognitoIdentityProviderClient> cognitoIdentityProviderClient =
AZ::Interface<IAWSClientAuthRequests>::Get()->GetCognitoIDPClient();
AZ::JobContext* jobContext = nullptr;
AWSCore::AWSCoreRequestBus::BroadcastResult(jobContext, &AWSCore::AWSCoreRequests::GetDefaultJobContext);
AZ::Job* confirmSignInJob = AZ::CreateJobFunction([this, cognitoIdentityProviderClient, confirmationCode, username]()
{
// Set Request parameters for SMS Multi factor authentication.
// Note: Email MFA is no longer supported by Cognito, use SMS as MFA
Aws::CognitoIdentityProvider::Model::RespondToAuthChallengeRequest respondToAuthChallengeRequest;
respondToAuthChallengeRequest.SetClientId(m_cognitoAppClientId.c_str());
respondToAuthChallengeRequest.AddChallengeResponses(CognitoSmsMfaCodeKey, confirmationCode.c_str());
respondToAuthChallengeRequest.AddChallengeResponses(CognitoUsernameKey, username.c_str());
respondToAuthChallengeRequest.SetChallengeName(Aws::CognitoIdentityProvider::Model::ChallengeNameType::SMS_MFA);
respondToAuthChallengeRequest.SetSession(m_session.c_str());
Aws::CognitoIdentityProvider::Model::RespondToAuthChallengeOutcome respondToAuthChallengeOutcome{ cognitoIdentityProviderClient->RespondToAuthChallenge(respondToAuthChallengeRequest) };
if (respondToAuthChallengeOutcome.IsSuccess())
{
Aws::CognitoIdentityProvider::Model::RespondToAuthChallengeResult respondToAuthChallengeResult{ respondToAuthChallengeOutcome.GetResult() };
if (respondToAuthChallengeResult.GetChallengeName() == Aws::CognitoIdentityProvider::Model::ChallengeNameType::NOT_SET)
{
Aws::CognitoIdentityProvider::Model::AuthenticationResultType authenticationResult = respondToAuthChallengeResult.GetAuthenticationResult();
UpdateTokens(authenticationResult);
AuthenticationProviderNotificationBus::Broadcast(
&AuthenticationProviderNotifications::OnPasswordGrantMultiFactorConfirmSignInSuccess, m_authenticationTokens);
}
}
else
{
Aws::Client::AWSError<Aws::CognitoIdentityProvider::CognitoIdentityProviderErrors> error = respondToAuthChallengeOutcome.GetError();
AuthenticationProviderNotificationBus::Broadcast(&AuthenticationProviderNotifications::OnPasswordGrantMultiFactorConfirmSignInFail, error.GetMessage().c_str());
}
}, true, jobContext);
confirmSignInJob->Start();
}
void AWSCognitoAuthenticationProvider::DeviceCodeGrantSignInAsync()
{
AZ_Assert(true, "Not supported");
}
void AWSCognitoAuthenticationProvider::DeviceCodeGrantConfirmSignInAsync()
{
AZ_Assert(true, "Not supported");
}
void AWSCognitoAuthenticationProvider::RefreshTokensAsync()
{
std::shared_ptr<Aws::CognitoIdentityProvider::CognitoIdentityProviderClient> cognitoIdentityProviderClient =
AZ::Interface<IAWSClientAuthRequests>::Get()->GetCognitoIDPClient();
AZ::JobContext* jobContext = nullptr;
AWSCore::AWSCoreRequestBus::BroadcastResult(jobContext, &AWSCore::AWSCoreRequests::GetDefaultJobContext);
AZ::Job* initiateAuthJob = AZ::CreateJobFunction([this, cognitoIdentityProviderClient]()
{
// Set Request parameters.
Aws::CognitoIdentityProvider::Model::InitiateAuthRequest initiateAuthRequest;
initiateAuthRequest.SetClientId(m_cognitoAppClientId.c_str());
initiateAuthRequest.SetAuthFlow(Aws::CognitoIdentityProvider::Model::AuthFlowType::REFRESH_TOKEN_AUTH);
// Set username and password for Password grant/ Initiate Auth flow.
Aws::Map<Aws::String, Aws::String> authParameters
{
{CognitoRefreshTokenAuthParamKey, GetAuthenticationTokens().GetRefreshToken().c_str()}
};
initiateAuthRequest.SetAuthParameters(authParameters);
Aws::CognitoIdentityProvider::Model::InitiateAuthOutcome initiateAuthOutcome{ cognitoIdentityProviderClient->InitiateAuth(initiateAuthRequest) };
if (initiateAuthOutcome.IsSuccess())
{
Aws::CognitoIdentityProvider::Model::InitiateAuthResult initiateAuthResult{ initiateAuthOutcome.GetResult() };
if (initiateAuthResult.GetChallengeName() == Aws::CognitoIdentityProvider::Model::ChallengeNameType::NOT_SET)
{
Aws::CognitoIdentityProvider::Model::AuthenticationResultType authenticationResult = initiateAuthResult.GetAuthenticationResult();
UpdateTokens(authenticationResult);
AuthenticationProviderNotificationBus::Broadcast(&AuthenticationProviderNotifications::OnRefreshTokensSuccess, m_authenticationTokens);
}
else
{
AuthenticationProviderNotificationBus::Broadcast(&AuthenticationProviderNotifications::OnRefreshTokensFail
, AZStd::string::format("Unexpected Challenge type: %s"
, Aws::CognitoIdentityProvider::Model::ChallengeNameTypeMapper::GetNameForChallengeNameType(initiateAuthResult.GetChallengeName()).c_str()));
}
}
else
{
Aws::Client::AWSError<Aws::CognitoIdentityProvider::CognitoIdentityProviderErrors> error = initiateAuthOutcome.GetError();
AuthenticationProviderNotificationBus::Broadcast(&AuthenticationProviderNotifications::OnRefreshTokensFail, error.GetMessage().c_str());
}
}, true, jobContext);
initiateAuthJob->Start();
}
// Call InitiateAuth for Cognito authentication flow.
// Refer https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-authentication-flow.html.
void AWSCognitoAuthenticationProvider::InitiateAuthInternalAsync(const AZStd::string& username, const AZStd::string& password
, AZStd::function<void(Aws::CognitoIdentityProvider::Model::InitiateAuthOutcome outcome)> outcomeCallback)
{
std::shared_ptr<Aws::CognitoIdentityProvider::CognitoIdentityProviderClient> cognitoIdentityProviderClient =
AZ::Interface<IAWSClientAuthRequests>::Get()->GetCognitoIDPClient();
AZ::JobContext* jobContext = nullptr;
AWSCore::AWSCoreRequestBus::BroadcastResult(jobContext, &AWSCore::AWSCoreRequests::GetDefaultJobContext);
AZ::Job* initiateAuthJob = AZ::CreateJobFunction([this, cognitoIdentityProviderClient, username, password, outcomeCallback]()
{
// Set Request parameters.
Aws::CognitoIdentityProvider::Model::InitiateAuthRequest initiateAuthRequest;
initiateAuthRequest.SetClientId(m_cognitoAppClientId.c_str());
initiateAuthRequest.SetAuthFlow(Aws::CognitoIdentityProvider::Model::AuthFlowType::USER_PASSWORD_AUTH);
// Set username and password for Password grant/ Initiate Auth flow.
Aws::Map<Aws::String, Aws::String> authParameters
{
{CognitoUsernameKey, username.c_str()},
{CognitoPasswordKey, password.c_str()}
};
initiateAuthRequest.SetAuthParameters(authParameters);
Aws::CognitoIdentityProvider::Model::InitiateAuthOutcome initiateAuthOutcome{ cognitoIdentityProviderClient->InitiateAuth(initiateAuthRequest) };
outcomeCallback(initiateAuthOutcome);
}, true, jobContext);
initiateAuthJob->Start();
}
void AWSCognitoAuthenticationProvider::UpdateTokens(const Aws::CognitoIdentityProvider::Model::AuthenticationResultType& authenticationResult)
{
m_authenticationTokens = AuthenticationTokens(authenticationResult.GetAccessToken().c_str(), authenticationResult.GetRefreshToken().c_str(),
authenticationResult.GetIdToken().c_str(), ProviderNameEnum::AWSCognitoIDP,
authenticationResult.GetExpiresIn());
}
} // namespace AWSClientAuth