Skip to content

Instantly share code, notes, and snippets.

@kirre-bylund
Last active February 9, 2024 07:41
Show Gist options
  • Save kirre-bylund/aff9f478879547655b6ef2df8810b324 to your computer and use it in GitHub Desktop.
Save kirre-bylund/aff9f478879547655b6ef2df8810b324 to your computer and use it in GitHub Desktop.
Full example implementation of Google Sign In class for Unreal Sign In
#include "GoogleSignIn.h"
#include "FromScratchSignIn.h"
#include "Interfaces/OnlineIdentityInterface.h"
#include "OnlineSubsystem.h"
IOnlineSubsystem* UGoogleSignIn::mOnlineSubsystem = nullptr;
FDelegateHandle UGoogleSignIn::LoginDelegateHandle = FDelegateHandle();
bool UGoogleSignIn::SignInWithGoogle(int LocalUserNumber, const FLoginCompletedCallback Callback)
{
if(!HasGoogleOnlineSubsystem())
{
return false;
}
if(mOnlineSubsystem->GetSubsystemName() != GOOGLE_SUBSYSTEM)
{
return false;
}
if(LoginDelegateHandle.IsValid())
{
// Login is already in progress
return false;
}
const IOnlineIdentityPtr IdentityInterface = mOnlineSubsystem->GetIdentityInterface();
if(IdentityInterface == nullptr || !IdentityInterface.IsValid())
{
return false;
}
if(IdentityInterface->GetLoginStatus(LocalUserNumber) == ELoginStatus::LoggedIn)
{
// User is already logged in
return false;
}
// Add our method to be called upon login process completion and keep track of the handle
LoginDelegateHandle = IdentityInterface->AddOnLoginCompleteDelegate_Handle(
LocalUserNumber, FOnLoginCompleteDelegate::CreateLambda(
[Callback](int LocalUserNumber, bool wasSuccessful, const FUniqueNetId& UserId, const FString& Error)
{
LoginCompletedHandler(LocalUserNumber, wasSuccessful, UserId, Error, Callback);
}));
IdentityInterface->Login(LocalUserNumber, FOnlineAccountCredentials{});
return true;
}
void UGoogleSignIn::LoginCompletedHandler(int LocalUserNumber, bool wasSuccessful, const FUniqueNetId& UserId, const FString& Error, const FLoginCompletedCallback Callback)
{
if (!HasGoogleOnlineSubsystem())
{
Callback.ExecuteIfBound(FLoginCompletedResponse{ false, TEXT("No Subsystem set"), LocalUserNumber });
return;
}
const IOnlineIdentityPtr IdentityInterface = mOnlineSubsystem->GetIdentityInterface();
if (IdentityInterface == nullptr || !IdentityInterface.IsValid())
{
Callback.ExecuteIfBound(FLoginCompletedResponse{ false, TEXT("Could not get Identity Interface"), LocalUserNumber });
return;
}
if (!LoginDelegateHandle.IsValid())
{
Callback.ExecuteIfBound(FLoginCompletedResponse{ false, TEXT("Login handle was invalid"), LocalUserNumber });
return;
}
// Clear the login handle and stop listening to callbacks for it
IdentityInterface->ClearOnLoginCompleteDelegate_Handle(LocalUserNumber, LoginDelegateHandle);
LoginDelegateHandle.Reset();
if (!wasSuccessful) {
AsyncTask(ENamedThreads::GameThread, [Callback, wasSuccessful, Error, LocalUserNumber]()
{
Callback.ExecuteIfBound(FLoginCompletedResponse{ wasSuccessful, Error, LocalUserNumber });
});
return;
}
const TSharedPtr<FUserOnlineAccount> UserAccount = IdentityInterface->GetUserAccount(UserId);
if(UserAccount == nullptr || !UserAccount.IsValid())
{
Callback.ExecuteIfBound(FLoginCompletedResponse{ false, TEXT("UserAccount was invalid"), LocalUserNumber });
return;
}
FGoogleUserData UserData{
UserAccount->GetRealName(),
UserAccount->GetDisplayName(),
"",
UserAccount->GetUserId()->ToString(),
"",
"",
""
};
UserAccount->GetAuthAttribute(AUTH_ATTR_ID_TOKEN, UserData.IdToken);
FLoginCompletedResponse LoginResponse{
wasSuccessful,
Error,
LocalUserNumber,
UserData
};
Callback.ExecuteIfBound(LoginResponse);
}
bool UGoogleSignIn::Initialize()
{
if (IOnlineSubsystem* OnlineSubsystem = IOnlineSubsystem::Get(GOOGLE_SUBSYSTEM))
{
if (OnlineSubsystem == nullptr)
{
return false;
}
if (!OnlineSubsystem->Init())
{
return false;
}
mOnlineSubsystem = OnlineSubsystem;
return true;
}
return false;
}
#pragma once
#include "CoreMinimal.h"
#include "OnlineSubsystem.h"
#include "GoogleSignIn.generated.h"
/**
*
*/
USTRUCT(BlueprintType)
struct FGoogleUserData
{
GENERATED_BODY()
/*
* The real name of the signed in user
*/
UPROPERTY(BlueprintReadWrite, Category = "Sign In | Google")
FString RealName;
/*
* The display name of the signed in user
*/
UPROPERTY(BlueprintReadWrite, Category = "Sign In | Google")
FString DisplayName;
/*
* The preferred display name of the signed in user
*/
UPROPERTY(BlueprintReadWrite, Category = "Sign In | Google")
FString PreferredDisplayName;
/*
* The ID of the signed in user
*/
UPROPERTY(BlueprintReadWrite, Category = "Sign In | Google")
FString ID;
/*
* The email of the signed in user
*/
UPROPERTY(BlueprintReadWrite, Category = "Sign In | Google")
FString Email;
/*
* The alias of the signed in user
*/
UPROPERTY(BlueprintReadWrite, Category = "Sign In | Google")
FString Alias;
/*
* The ID Token of the signed in user
*/
UPROPERTY(BlueprintReadWrite, Category = "Sign In | Google")
FString IdToken;
};
/**
* Response from a login process
*/
USTRUCT(BlueprintType)
struct FLoginCompletedResponse
{
GENERATED_BODY()
/*
* Did the login process succeed
*/
UPROPERTY(BlueprintReadWrite, Category="Sign In | Google")
bool success;
/*
* If the process did not succeed, this field holds any errors
*/
UPROPERTY(BlueprintReadWrite, Category = "Sign In | Google")
FString Error;
/*
* Which local user is this for
*/
UPROPERTY(BlueprintReadWrite, Category = "Sign In | Google")
int LocalUseNumber;
/*
* On successful login, this holds the signed in user's data
*/
UPROPERTY(BlueprintReadWrite, Category = "Sign In | Google")
FGoogleUserData UserData;
};
/*
* Delegate for responses upon login process completion
*/
DECLARE_DYNAMIC_DELEGATE_OneParam(FLoginCompletedCallback, FLoginCompletedResponse, LoginResponse);
UCLASS(Blueprintable)
class UGoogleSignIn : public UObject
{
GENERATED_BODY()
public:
/**
* Initialize the Google subsystem to enable Google authentication
* Returns true if initialization succeeded
*/
UFUNCTION(BlueprintCallable, Category = "Sign In | Google")
static bool Initialize();
/**
* Does the Google Subsystem exist and is initialized
*/
UFUNCTION(BlueprintCallable, Category = "Sign In | Google")
static bool HasGoogleOnlineSubsystem() { return mOnlineSubsystem != nullptr; }
/**
* Do sign in using Google asynchronously and call callback on completion
*
* <param name="LocalUserNumber">Which local user to do sign in for</param>
* <param name="Callback">Callback utilized when method is called from Blueprints</param>
*/
UFUNCTION(BlueprintCallable, Category = "Sign In | Google")
static bool SignInWithGoogle(int LocalUserNumber, const FLoginCompletedCallback Callback);
private:
static void LoginCompletedHandler(int LocalUserNumber, bool wasSuccessful, const FUniqueNetId& UserId, const FString& Error, const FLoginCompletedCallback BPCallback);
// Handle for keeping track of a google subsystem login process
static FDelegateHandle LoginDelegateHandle;
static IOnlineSubsystem* mOnlineSubsystem;
};
// Add the generic Online Subsystem to your project
PrivateDependencyModuleNames.AddRange(new string[] { "OnlineSubsystem" });
// Dynamically add Google Online Subsystem for platforms where you intend to use it
if (Target.Platform == UnrealTargetPlatform.Android)
{
DynamicallyLoadedModuleNames.Add("OnlineSubsystemGoogle");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment