// Copyright Epic Games, Inc. All Rights Reserved. #if USES_RESTFUL_GOOGLE #include "OnlineExternalUIInterfaceGoogleRest.h" #include "OnlineSubsystemGoogle.h" #include "OnlineIdentityGoogleRest.h" #include "OnlineError.h" #define GOOGLE_STATE_TOKEN TEXT("state") #define GOOGLE_ACCESS_TOKEN TEXT("code") #define GOOGLE_ERRORCODE_TOKEN TEXT("error") #define GOOGLE_ERRORCODE_DENY TEXT("access_denied") bool FOnlineExternalUIGoogle::ShowLoginUI(const int ControllerIndex, bool bShowOnlineOnly, bool bShowSkipButton, const FOnLoginUIClosedDelegate& Delegate) { FString ErrorStr; bool bStarted = false; if (ControllerIndex >= 0 && ControllerIndex < MAX_LOCAL_PLAYERS) { FOnlineIdentityGooglePtr IdentityInt = StaticCastSharedPtr(GoogleSubsystem->GetIdentityInterface()); if (IdentityInt.IsValid()) { IdentityInt->RetrieveDiscoveryDocument([this, ControllerIndex, Delegate](bool bWasSuccessful) { FString ErrorStr; if (bWasSuccessful) { FOnlineIdentityGooglePtr IdentityInt = StaticCastSharedPtr(GoogleSubsystem->GetIdentityInterface()); IdentityInt->UpdateLoginURLWithEndpointData(); const FGoogleLoginURL& URLDetails = IdentityInt->GetLoginURLDetails(); if (URLDetails.IsValid()) { const FString RequestedURL = URLDetails.GetURL(); bool bShouldContinueLoginFlow = false; FOnLoginRedirectURL OnRedirectURLDelegate = FOnLoginRedirectURL::CreateRaw(this, &FOnlineExternalUIGoogle::OnLoginRedirectURL); FOnLoginFlowComplete OnExternalLoginFlowCompleteDelegate = FOnLoginFlowComplete::CreateRaw(this, &FOnlineExternalUIGoogle::OnExternalLoginFlowComplete, ControllerIndex, Delegate); TriggerOnLoginFlowUIRequiredDelegates(RequestedURL, OnRedirectURLDelegate, OnExternalLoginFlowCompleteDelegate, bShouldContinueLoginFlow); if (bShouldContinueLoginFlow) { return; } else { ErrorStr = TEXT("ShowLoginUI: Login Flow interrupted"); } } else { ErrorStr = TEXT("ShowLoginUI: Url Details not properly configured"); } } else { ErrorStr = TEXT("ShowLoginUI: Could not retrieve Discovery Document"); } FOnlineError Error; Error.SetFromErrorCode(TEXT("")); GoogleSubsystem->ExecuteNextTick([ControllerIndex, Delegate, Error = MoveTemp(Error)]() { Delegate.ExecuteIfBound(nullptr, ControllerIndex, Error); }); }); bStarted = true; } else { ErrorStr = TEXT("ShowLoginUI: Missing identity interface"); } } else { ErrorStr = FString::Printf(TEXT("ShowLoginUI: Invalid controller index (%d)"), ControllerIndex); } if (!bStarted) { UE_LOG_ONLINE_EXTERNALUI(Warning, TEXT("%s"), *ErrorStr); FOnlineError Error; Error.SetFromErrorCode(MoveTemp(ErrorStr)); GoogleSubsystem->ExecuteNextTick([ControllerIndex, Delegate, Error = MoveTemp(Error)]() { Delegate.ExecuteIfBound(nullptr, ControllerIndex, Error); }); } return bStarted; } FLoginFlowResult FOnlineExternalUIGoogle::OnLoginRedirectURL(const FString& RedirectURL) { FLoginFlowResult Result; FOnlineIdentityGooglePtr IdentityInt = StaticCastSharedPtr(GoogleSubsystem->GetIdentityInterface()); if (IdentityInt.IsValid()) { const FGoogleLoginURL& URLDetails = IdentityInt->GetLoginURLDetails(); if (URLDetails.IsValid()) { // Wait for the RedirectURI to appear if (!RedirectURL.Contains(FPlatformHttp::UrlEncode(URLDetails.LoginUrl)) && RedirectURL.StartsWith(URLDetails.LoginRedirectUrl)) { TMap ParamsMap; { FString URLPrefix; FString ParamsOnly; if (!RedirectURL.Split(TEXT("?"), &URLPrefix, &ParamsOnly)) { ParamsOnly = RedirectURL; } if (ParamsOnly[ParamsOnly.Len() - 1] == TEXT('#')) { ParamsOnly[ParamsOnly.Len() - 1] = TEXT('\0'); } TArray Params; ParamsOnly.ParseIntoArray(Params, TEXT("&")); for (FString& Param : Params) { FString Key, Value; if (Param.Split(TEXT("="), &Key, &Value)) { ParamsMap.Add(Key, Value); } } } const FString* State = ParamsMap.Find(GOOGLE_STATE_TOKEN); if ((URLDetails.State.IsEmpty() && !State) || (!URLDetails.State.IsEmpty() && State && URLDetails.State == *State)) { const FString* AccessToken = ParamsMap.Find(GOOGLE_ACCESS_TOKEN); if (AccessToken) { Result.Error.bSucceeded = true; Result.Token = *AccessToken; } else { const FString* ErrorCode = ParamsMap.Find(GOOGLE_ERRORCODE_TOKEN); if (ErrorCode) { if (*ErrorCode == GOOGLE_ERRORCODE_DENY) { Result.Error.ErrorRaw = LOGIN_CANCELLED; Result.Error.ErrorMessage = FText::FromString(LOGIN_CANCELLED); Result.Error.ErrorCode = LOGIN_CANCELLED; Result.Error.ErrorMessage = NSLOCTEXT("GoogleAuth", "GoogleAuthDeny", "Google Auth Denied"); Result.NumericErrorCode = -1; } else { Result.Error.ErrorRaw = RedirectURL; Result.Error.ErrorCode = *ErrorCode; // there is no descriptive error text Result.Error.ErrorMessage = NSLOCTEXT("GoogleAuth", "GoogleAuthError", "Google Auth Error"); // there is no error code Result.NumericErrorCode = 0; } } else { // Set some default in case parsing fails Result.Error.ErrorRaw = LOGIN_ERROR_UNKNOWN; Result.Error.ErrorMessage = FText::FromString(LOGIN_ERROR_UNKNOWN); Result.Error.ErrorCode = LOGIN_ERROR_UNKNOWN; Result.NumericErrorCode = -2; } } } else { // Set some default in case parsing fails Result.Error.ErrorRaw = LOGIN_ERROR_UNKNOWN; Result.Error.ErrorMessage = FText::FromString(LOGIN_ERROR_UNKNOWN); Result.Error.ErrorCode = LOGIN_ERROR_UNKNOWN; Result.NumericErrorCode = -2; } } } } return Result; } void FOnlineExternalUIGoogle::OnExternalLoginFlowComplete(const FLoginFlowResult& Result, int ControllerIndex, const FOnLoginUIClosedDelegate Delegate) { UE_LOG_ONLINE_EXTERNALUI(Log, TEXT("OnExternalLoginFlowComplete %s"), *Result.ToDebugString()); bool bStarted = false; if (Result.IsValid()) { FOnlineIdentityGooglePtr IdentityInt = StaticCastSharedPtr(GoogleSubsystem->GetIdentityInterface()); if (IdentityInt.IsValid()) { bStarted = true; FOnLoginCompleteDelegate CompletionDelegate; CompletionDelegate = FOnLoginCompleteDelegate::CreateRaw(this, &FOnlineExternalUIGoogle::OnAccessTokenLoginComplete, Delegate); FAuthTokenGoogle AuthToken(Result.Token, EGoogleExchangeToken::GoogleExchangeToken); IdentityInt->Login(ControllerIndex, AuthToken, CompletionDelegate); } } if (!bStarted) { FOnlineError LoginFlowError = Result.Error; GoogleSubsystem->ExecuteNextTick([ControllerIndex, LoginFlowError, Delegate]() { Delegate.ExecuteIfBound(nullptr, ControllerIndex, LoginFlowError); }); } } void FOnlineExternalUIGoogle::OnAccessTokenLoginComplete(int32 LocalUserNum, bool bWasSuccessful, const FUniqueNetId& UserId, const FString& Error, FOnLoginUIClosedDelegate Delegate) { FUniqueNetIdPtr StrongUserId = UserId.AsShared(); GoogleSubsystem->ExecuteNextTick([StrongUserId, LocalUserNum, bWasSuccessful, Delegate]() { Delegate.ExecuteIfBound(StrongUserId, LocalUserNum, FOnlineError(bWasSuccessful)); }); } #endif // USES_RESTFUL_GOOGLE