The Supabase Unreal Engine plugin provides comprehensive session handling through a robust persistence system that automatically manages user authentication state, connection management, and seamless session restoration across game sessions. The system is built around the USupabaseSessionSave class and integrated deeply with the USupabaseSubsystem for centralized state management.
USupabaseSessionSave
USaveGameUSupabaseSubsystem
USupabaseUtils
UCLASS()
class SUPABASE_API USupabaseSessionSave : public USaveGame
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, Category = "Supabase|Session")
USupabaseConnection* SupabaseConnection;
UPROPERTY(EditAnywhere, Category = "Supabase|Session")
FString RefreshToken;
UPROPERTY(EditAnywhere, Category = "Supabase|Session")
FString Email;
UPROPERTY(EditAnywhere, Category = "Supabase|Session")
FString Password;
UPROPERTY(EditAnywhere, Category = "Supabase|Session")
FDateTime LastLogin;
UPROPERTY(EditAnywhere, Category = "Supabase|Session")
FUser User;
UPROPERTY(EditAnywhere, Category = "Supabase|Session")
bool bIsLoggedIn;
};
Connection Data
USupabaseConnection object with credentialsUser Information
FUser structure with profile dataSession Metadata
Initialization Phase
Active Session Phase
Termination Phase
bool USupabaseSubsystem::InitializeConnection(USupabaseConnection* Connection)
{
// ... connection setup ...
// Try to restore previous session
FSupabaseCredentials SavedCredentials;
FUser SavedUser;
if (USupabaseUtils::LoadSupabaseSession(SavedCredentials, SavedUser))
{
UE_LOG(LogTemp, Log, TEXT("Restored previous Supabase session"));
Connection->SupabaseCredentials.UserAuthenticatedKey = SavedCredentials.UserAuthenticatedKey;
Connection->SupabaseCredentials.RefreshToken = SavedCredentials.RefreshToken;
SetAuthenticationState(true, SavedUser);
StartConnectionHealthChecks();
}
return true;
}
Automatic Saving
Manual Saving
bool USupabaseUtils::SaveSupabaseSession(const FSupabaseCredentials& Credentials, const FUser& User)
{
USupabaseSessionSave* SaveGameInstance = Cast<USupabaseSessionSave>(
UGameplayStatics::CreateSaveGameObject(USupabaseSessionSave::StaticClass()));
// Create temporary connection for credentials storage
USupabaseConnection* TempConnection = NewObject<USupabaseConnection>();
TempConnection->SupabaseCredentials = Credentials;
SaveGameInstance->SupabaseConnection = TempConnection;
SaveGameInstance->User = User;
SaveGameInstance->LastLogin = FDateTime::Now();
SaveGameInstance->bIsLoggedIn = !Credentials.UserAuthenticatedKey.IsEmpty();
return UGameplayStatics::SaveGameToSlot(SaveGameInstance, TEXT("SupabaseSession"), 0);
}
Automatic Loading
Manual Loading
bool USupabaseUtils::LoadSupabaseSession(FSupabaseCredentials& OutCredentials, FUser& OutUser)
{
USupabaseSessionSave* SaveGameInstance = Cast<USupabaseSessionSave>(
UGameplayStatics::LoadGameFromSlot(TEXT("SupabaseSession"), 0));
if (!SaveGameInstance || !SaveGameInstance->SupabaseConnection)
{
return false;
}
OutCredentials = SaveGameInstance->SupabaseConnection->SupabaseCredentials;
OutUser = SaveGameInstance->User;
// Validate loaded credentials
if (OutCredentials.UserAuthenticatedKey.IsEmpty())
{
UE_LOG(LogTemp, Warning, TEXT("Loaded session has empty authentication key."));
return false;
}
return true;
}
Automatic Clearing
Manual Clearing
bool USupabaseUtils::ClearSupabaseSession()
{
if (UGameplayStatics::DoesSaveGameExist(TEXT("SupabaseSession"), 0))
{
return UGameplayStatics::DeleteGameInSlot(TEXT("SupabaseSession", 0);
}
return true; // No session to clear
}
State Variables
// USupabaseSubsystem state management
bool bIsConnected; // Network connectivity status
bool bIsAuthenticated; // User authentication status
bool bIsInitialized; // Subsystem initialization status
bool bDebugLogging; // Debug output control
State Transitions
State Management
void USupabaseSubsystem::SetAuthenticationState(bool bAuthenticated, const FUser& User)
{
bool bStateChanged = (bIsAuthenticated != bAuthenticated);
bIsAuthenticated = bAuthenticated;
if (bAuthenticated)
{
CurrentUser = User;
if (bIsConnected)
{
StartConnectionHealthChecks();
}
}
else
{
CurrentUser = FUser();
StopConnectionHealthChecks();
}
if (bStateChanged)
{
OnAuthenticationStateChanged.Broadcast(bAuthenticated, CurrentUser);
}
}
Token Validation
Connection Validation
bool USupabaseUtils::ValidateSupabaseConnection(USupabaseConnection* Connection, FString& OutErrorMessage)
{
if (!Connection)
{
OutErrorMessage = TEXT("Connection is null");
return false;
}
if (Connection->SupabaseServerUrl.IsEmpty() || !IsValidSupabaseURL(Connection->SupabaseServerUrl))
{
OutErrorMessage = TEXT("Invalid server URL");
return false;
}
if (Connection->SupabaseCredentials.AnonymousKey.IsEmpty() ||
!IsValidSupabaseKey(Connection->SupabaseCredentials.AnonymousKey))
{
OutErrorMessage = TEXT("Invalid API key");
return false;
}
return true;
}
Health Check System
Health Check Implementation
void USupabaseSubsystem::OnConnectionHealthCheck()
{
if (bDebugLogging)
{
UE_LOG(LogTemp, Verbose, TEXT("Performing connection health check"));
}
TestConnection();
}
void USupabaseSubsystem::OnTokenRefreshCheck()
{
if (bIsAuthenticated && !ActiveConnection->SupabaseCredentials.RefreshToken.IsEmpty())
{
RefreshToken();
}
}
Connection Events
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnConnectionStateChanged, bool, bIsConnected);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnAuthenticationStateChanged, bool, bIsAuthenticated, const FUser&, User);
Authentication Events
OnLoginSuccessful: Triggered on successful authenticationOnLoginFailed: Triggered on authentication failureOnLogoutSuccessful: Triggered on successful logoutOnLogoutFailed: Triggered on logout failureSession Events
OnConnectionStateChanged: Connection status changesOnAuthenticationStateChanged: Authentication status changesOnUserRetrieved: User data successfully loadedOnRequestSuccess/Failure: General request status updatesSubsystem Event Handlers
void USupabaseSubsystem::HandleLoginSuccess(FString Response, const FTokenResponse& TokenResponse)
{
SetConnectionState(true);
SetAuthenticationState(true, TokenResponse.User);
OnLoginSuccessful.Broadcast(Response, TokenResponse);
}
void USupabaseSubsystem::HandleLoginFailure(FString Error)
{
SetAuthenticationState(false);
OnLoginFailed.Broadcast(Error);
}
Token Refresh Flow
Connection Recovery
User Switching
Session Persistence
Sensitive Data Handling
Session Security
void USupabaseSubsystem::ClearCache()
{
// Clear save game
if (UGameplayStatics::DoesSaveGameExist(TEXT("SupabaseSession"), 0))
{
UGameplayStatics::DeleteGameInSlot(TEXT("SupabaseSession"), 0);
}
// Reset state
SetAuthenticationState(false);
CurrentUser = FUser();
// Clear credentials
if (ActiveConnection)
{
ActiveConnection->SupabaseCredentials.UserAuthenticatedKey.Empty();
ActiveConnection->SupabaseCredentials.RefreshToken.Empty();
}
}
Token Security
Network Security
Session Status
FString USupabaseSubsystem::GetConnectionStatus() const
{
if (!bIsInitialized)
return TEXT("Not Initialized");
if (!bIsConnected)
return FString::Printf(TEXT("Disconnected (Retries: %d/%d)"),
ConnectionRetryCount, MaxConnectionRetries);
if (bIsAuthenticated)
return FString::Printf(TEXT("Connected & Authenticated (%s)"), *CurrentUser.Email);
return TEXT("Connected (Not Authenticated)");
}
Debug Logging
Common Issues
Diagnostic Tools
// Initialize session system
USupabaseSubsystem* Subsystem = GetGameInstance()->GetSubsystem<USupabaseSubsystem>();
Subsystem->InitializeConnection(MyConnection);
// Check session status
if (Subsystem->IsAuthenticated())
{
FUser CurrentUser = Subsystem->GetCurrentUser();
// User is logged in, proceed with authenticated operations
}
// Manual session operations
USupabaseUtils::SaveSupabaseSession(Credentials, User);
USupabaseUtils::LoadSupabaseSession(OutCredentials, OutUser);
USupabaseUtils::ClearSupabaseSession();
// Bind to session events
Subsystem->OnAuthenticationStateChanged.AddDynamic(this, &AMyActor::OnAuthChanged);
Subsystem->OnConnectionStateChanged.AddDynamic(this, &AMyActor::OnConnectionChanged);
// Event handlers
void AMyActor::OnAuthChanged(bool bIsAuthenticated, const FUser& User)
{
if (bIsAuthenticated)
{
// Update UI for logged-in state
UpdateUserInterface(User);
}
else
{
// Show login screen
ShowLoginDialog();
}
}
This comprehensive session handling system ensures seamless user experience with automatic state management, robust error recovery, and secure data persistence across all platforms supported by Unreal Engine.