Version 1.34.2+ | Production Ready | Unreal Engine 5.5+
The Supabase UE5 Plugin provides robust email/password authentication with comprehensive error handling, session management, and Blueprint-friendly interfaces. This authentication system integrates seamlessly with Supabase’s built-in authentication service.
The email/password authentication system offers:
Before implementing email/password authentication, ensure:
The UAuthenticationProvider class manages different authentication methods:
UENUM(BlueprintType)
enum class EAuthProvider : uint8
{
Email UMETA(DisplayName = "Email"),
Google UMETA(DisplayName = "Google"),
Facebook UMETA(DisplayName = "Facebook"),
GitHub UMETA(DisplayName = "GitHub"),
Twitter UMETA(DisplayName = "Twitter")
};
Multiple registration modes are supported through ERegistrationMode:
UENUM(BlueprintType)
enum class ERegistrationMode : uint8
{
EmailPassword UMETA(DisplayName = "Email & Password"),
EmailPasswordConfirm UMETA(DisplayName = "Email & Password with Confirmation"),
PhonePassword UMETA(DisplayName = "Phone & Password"),
EmailOTP UMETA(DisplayName = "Email with OTP"),
PhoneOTP UMETA(DisplayName = "Phone with OTP")
};
// Simple login through manager
USupabaseManager::LoginWithEmail(this, TEXT("user@example.com"), TEXT("password"));
// With event binding
USupabaseSubsystem* Supabase = USupabaseManager::GetSupabaseSubsystem(this);
if (Supabase)
{
// Bind authentication events
Supabase->OnLoginSuccessful.AddDynamic(this, &AMyClass::OnLoginSuccess);
Supabase->OnLoginFailed.AddDynamic(this, &AMyClass::OnLoginFailure);
// Perform login
Supabase->LoginWithEmail(TEXT("user@example.com"), TEXT("password"));
}
// Async login with custom handling
UAsyncLogin* LoginNode = UAsyncLogin::LoginWithEmailAsync(this, TEXT("user@example.com"), TEXT("password"));
LoginNode->OnSuccess.AddDynamic(this, &AMyClass::OnLoginSuccess);
LoginNode->OnFailure.AddDynamic(this, &AMyClass::OnLoginFailure);
// Advanced login with specific connection
UAsyncLogin* AdvancedLogin = UAsyncLogin::LoginWithEmailAdvanced(this, Connection, Email, Password);
// Using subsystem
USupabaseSubsystem* Supabase = USupabaseManager::GetSupabaseSubsystem(this);
if (Supabase)
{
Supabase->OnLoginSuccessful.AddDynamic(this, &AMyClass::OnRegistrationSuccess);
Supabase->OnLoginFailed.AddDynamic(this, &AMyClass::OnRegistrationFailure);
Supabase->RegisterWithEmail(TEXT("newuser@example.com"), TEXT("securepassword"));
}
// Using async register with confirmation
UAsyncRegister* RegisterNode = UAsyncRegister::RegisterWithConfirmationAsync(
this,
TEXT("user@example.com"),
TEXT("password"),
TEXT("password") // Password confirmation
);
RegisterNode->OnSuccess.AddDynamic(this, &AMyClass::OnRegistrationSuccess);
RegisterNode->OnFailure.AddDynamic(this, &AMyClass::OnRegistrationFailure);
RegisterNode->OnRegistrationPending.AddDynamic(this, &AMyClass::OnEmailConfirmationPending);
// Configure registration options
FRegistrationOptions Options;
Options.bRequireEmailConfirmation = true;
Options.EmailConfirmationRedirectURL = TEXT("https://yourgame.com/confirm");
Options.MinPasswordLength = 8;
Options.bRequireSpecialCharacters = true;
Options.bRequireNumbers = true;
Options.bRequireUppercase = true;
// Add custom user metadata
Options.UserMetadata.Add(TEXT("display_name"), TEXT("Player One"));
Options.UserMetadata.Add(TEXT("preferred_language"), TEXT("en"));
UAsyncRegister* RegisterNode = UAsyncRegister::RegisterWithModeAsync(
this,
TEXT("user@example.com"),
TEXT("securepassword123!"),
ERegistrationMode::EmailPasswordConfirm
);
// Email validation
bool bIsValidEmail = UAsyncRegister::ValidateEmailFormat(UserEmailInput);
// Phone validation
bool bIsValidPhone = UAsyncRegister::ValidatePhoneFormat(UserPhoneInput);
// Password strength validation
bool bIsStrongPassword = UAsyncRegister::ValidatePasswordStrength(UserPassword, Options);
// Generate secure password
FString SecurePassword = UAsyncRegister::GenerateSecurePassword(12);
UFUNCTION()
void AMyClass::OnLoginSuccess(FString Response, const FTokenResponse& TokenResponse)
{
UE_LOG(LogTemp, Log, TEXT("Login successful for user: %s"), *TokenResponse.User.Email);
// Access user information
FString UserID = TokenResponse.User.Id;
FString UserEmail = TokenResponse.User.Email;
FString AccessToken = TokenResponse.AccessToken;
// Store or use tokens as needed
CurrentUser = TokenResponse.User;
// Navigate to main game or update UI
// ...
}
UFUNCTION()
void AMyClass::OnRegistrationSuccess(FString Response, const FTokenResponse& TokenResponse)
{
UE_LOG(LogTemp, Log, TEXT("Registration successful"));
// Handle successful registration
}
UFUNCTION()
void AMyClass::OnLoginFailure(FString Error)
{
UE_LOG(LogTemp, Warning, TEXT("Login failed: %s"), *Error);
// Handle specific error cases
if (Error.Contains(TEXT("Invalid login credentials")))
{
// Show invalid credentials message
ShowErrorMessage(TEXT("Invalid email or password"));
}
else if (Error.Contains(TEXT("Email not confirmed")))
{
// Prompt user to check email
ShowEmailConfirmationPrompt();
}
else if (Error.Contains(TEXT("Too many requests")))
{
// Rate limiting active
ShowRateLimitMessage();
}
else
{
// Generic error handling
ShowErrorMessage(FString::Printf(TEXT("Login failed: %s"), *Error));
}
}
UFUNCTION()
void AMyClass::OnEmailConfirmationPending(const FString& Message)
{
UE_LOG(LogTemp, Log, TEXT("Email confirmation required: %s"), *Message);
// Show confirmation dialog to user
ShowEmailConfirmationDialog(Message);
}
// Session is automatically restored on subsystem initialization
void USupabaseSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
// ... initialization code ...
// 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();
}
}
// Listen for authentication state changes
USupabaseSubsystem* Supabase = USupabaseManager::GetSupabaseSubsystem(this);
if (Supabase)
{
Supabase->OnAuthStateChanged.AddDynamic(this, &AMyClass::OnAuthenticationStateChanged);
}
UFUNCTION()
void AMyClass::OnAuthenticationStateChanged(bool bIsAuthenticated, const FUser& User)
{
if (bIsAuthenticated)
{
UE_LOG(LogTemp, Log, TEXT("User authenticated: %s"), *User.Email);
// User logged in - update UI, load user data, etc.
}
else
{
UE_LOG(LogTemp, Warning, TEXT("User logged out or session expired"));
// User logged out - return to login screen, clear data, etc.
}
}
bool UAsyncLogin::ValidateInputs()
{
if (!IsValid(WorldContextObject))
{
UE_LOG(LogTemp, Error, TEXT("AsyncLogin: Invalid WorldContextObject"));
OnFailure.Broadcast(TEXT("Invalid world context"));
return false;
}
if (Email.IsEmpty() || Password.IsEmpty())
{
UE_LOG(LogTemp, Error, TEXT("AsyncLogin: Email or Password is empty"));
OnFailure.Broadcast(TEXT("Email and password cannot be empty"));
return false;
}
// Validate email format
if (!Email.Contains(TEXT("@")))
{
UE_LOG(LogTemp, Error, TEXT("AsyncLogin: Invalid email format"));
OnFailure.Broadcast(TEXT("Invalid email format"));
return false;
}
// Password length check
if (Password.Len() < 6)
{
UE_LOG(LogTemp, Error, TEXT("AsyncLogin: Password too short"));
OnFailure.Broadcast(TEXT("Password must be at least 6 characters"));
return false;
}
return true;
}
// Registration options for password security
FRegistrationOptions SecurityOptions;
SecurityOptions.MinPasswordLength = 8;
SecurityOptions.bRequireSpecialCharacters = true;
SecurityOptions.bRequireNumbers = true;
SecurityOptions.bRequireUppercase = true;
// Domain validation for registration
bool bIsValidDomain = UAsyncRegister::IsValidDomain(Email);
bool bHasProhibitedContent = UAsyncRegister::ContainsProhibitedContent(Email);
// ✅ Good - Use subsystem for authentication
USupabaseSubsystem* Supabase = USupabaseManager::GetSupabaseSubsystem(this);
Supabase->LoginWithEmail(Email, Password);
// ❌ Bad - Creating individual clients
USupabaseClient* Client = NewObject<USupabaseClient>(); // Creates memory leaks
// ✅ Good - Comprehensive error handling
UFUNCTION()
void ALoginWidget::OnLoginFailure(FString Error)
{
// Hide loading indicator
SetLoginButtonEnabled(true);
// Parse and display appropriate error message
if (Error.Contains(TEXT("Invalid login credentials")))
{
ShowErrorText(TEXT("Invalid email or password. Please try again."));
HighlightField(EmailField);
HighlightField(PasswordField);
}
else if (Error.Contains(TEXT("Email not confirmed")))
{
ShowEmailConfirmationDialog();
}
// ... handle other error cases
}
// ✅ Good - Never store passwords in memory longer than necessary
void ALoginWidget::AttemptLogin()
{
FString Email = EmailInputField->GetText().ToString();
FString Password = PasswordInputField->GetText().ToString();
// Clear password field immediately
PasswordInputField->SetText(FText::GetEmpty());
// Use credentials immediately
USupabaseManager::LoginWithEmail(this, Email, Password);
// Don't store password in member variables
}
// ✅ Good - Provide user feedback during async operations
void ALoginWidget::StartLogin()
{
// Disable UI during login
SetLoginButtonEnabled(false);
ShowLoadingIndicator();
// Bind events before starting login
USupabaseSubsystem* Supabase = USupabaseManager::GetSupabaseSubsystem(this);
if (Supabase)
{
Supabase->OnLoginSuccessful.AddDynamic(this, &ALoginWidget::OnLoginSuccess);
Supabase->OnLoginFailed.AddDynamic(this, &ALoginWidget::OnLoginFailure);
Supabase->LoginWithEmail(Email, Password);
}
}
| Error Message | Cause | Solution |
|---|---|---|
Invalid login credentials |
Wrong email/password | Verify credentials, check caps lock |
Email not confirmed |
User hasn’t confirmed email | Resend confirmation email |
Too many requests |
Rate limiting active | Wait before retrying |
Email and password cannot be empty |
Missing input | Validate form inputs |
Invalid email format |
Malformed email | Validate email format |
Password must be at least 6 characters |
Weak password | Enforce password requirements |
No active connection |
Supabase not initialized | Check connection setup |
// Enable detailed logging
USupabaseManager::EnableDebugLogging(this, true);
// Check authentication state
USupabaseSubsystem* Supabase = USupabaseManager::GetSupabaseSubsystem(this);
if (Supabase)
{
UE_LOG(LogTemp, Log, TEXT("Is Authenticated: %s"),
Supabase->IsAuthenticated() ? TEXT("Yes") : TEXT("No"));
UE_LOG(LogTemp, Log, TEXT("Current User: %s"),
*Supabase->GetCurrentUser().Email);
}
// Validate connection
FString ErrorMessage;
if (!USupabaseUtils::ValidateSupabaseConnection(Connection, ErrorMessage))
{
UE_LOG(LogTemp, Error, TEXT("Connection validation failed: %s"), *ErrorMessage);
}
This documentation is maintained for Supabase UE5 Plugin version 1.34.2+. For the latest updates and additional features, visit the official wiki.