Skip to content

Commit 247b2ea

Browse files
authored
Add SIWE login async task and improve OAuth browser flow (#37)
Introduces UAsyncTaskThirdwebLoginWithSiwe for high-level SIWE login handling, including browser widget orchestration and authentication flow. Refactors OAuth browser and external browser classes to support SIWE, corrects parameter order and provider string casing, and improves resource cleanup and thread safety. Increases HTTP request timeout to 15 seconds and updates relevant delegate signatures and logic for SIWE payload/signature handling. Closes BLD-445 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **New Features** * Added Sign-In with Ethereum (SIWE) in-app login functionality with browser-based authentication flow. * **Improvements** * Enhanced authentication session management with improved cleanup and resource handling. * Improved thread safety for authentication callbacks. * Increased HTTP request timeout duration for better reliability. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 3a009e7 commit 247b2ea

File tree

9 files changed

+669
-457
lines changed

9 files changed

+669
-457
lines changed
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Copyright (c) 2025 Thirdweb. All Rights Reserved.
2+
3+
#include "AsyncTasks/Wallets/InApp/AsyncTaskThirdwebLoginWithSiwe.h"
4+
5+
#include "Async/TaskGraphInterfaces.h"
6+
#include "Blueprint/UserWidget.h"
7+
#include "Browser/ThirdwebOAuthBrowserUserWidget.h"
8+
#include "Engine/World.h"
9+
#include "Kismet/GameplayStatics.h"
10+
#include "ThirdwebLog.h"
11+
12+
void UAsyncTaskThirdwebLoginWithSiwe::Activate()
13+
{
14+
Browser->OnSiweComplete.AddDynamic(this, &ThisClass::HandleSiweComplete);
15+
Browser->OnError.AddDynamic(this, &ThisClass::HandleFailed);
16+
Browser->AddToViewport(10000);
17+
Browser->Authenticate(Wallet);
18+
}
19+
20+
UAsyncTaskThirdwebLoginWithSiwe* UAsyncTaskThirdwebLoginWithSiwe::LoginWithSiwe(UObject* WorldContextObject, const FInAppWalletHandle& Wallet)
21+
{
22+
if (!WorldContextObject)
23+
{
24+
return nullptr;
25+
}
26+
NEW_TASK
27+
Task->Wallet = Wallet;
28+
Task->Browser = CreateWidget<UThirdwebOAuthBrowserUserWidget>(UGameplayStatics::GetGameInstance(WorldContextObject),
29+
UThirdwebOAuthBrowserUserWidget::StaticClass());
30+
Task->RegisterWithGameInstance(WorldContextObject);
31+
return Task;
32+
}
33+
34+
void UAsyncTaskThirdwebLoginWithSiwe::HandleSiweComplete(const FString& Payload, const FString& Signature)
35+
{
36+
if (IsInGameThread())
37+
{
38+
Success.Broadcast(Payload, Signature);
39+
Browser->RemoveFromParent();
40+
SetReadyToDestroy();
41+
}
42+
else
43+
{
44+
// Retry on the GameThread.
45+
TWeakObjectPtr<UAsyncTaskThirdwebLoginWithSiwe> WeakThis = this;
46+
FFunctionGraphTask::CreateAndDispatchWhenReady([WeakThis, Payload, Signature]()
47+
{
48+
if (WeakThis.IsValid())
49+
{
50+
WeakThis->HandleSiweComplete(Payload, Signature);
51+
}
52+
},
53+
TStatId(),
54+
nullptr,
55+
ENamedThreads::GameThread);
56+
}
57+
}
58+
59+
void UAsyncTaskThirdwebLoginWithSiwe::HandleFailed(const FString& Error)
60+
{
61+
if (IsInGameThread())
62+
{
63+
Browser->RemoveFromParent();
64+
Failed.Broadcast(Error);
65+
SetReadyToDestroy();
66+
}
67+
else
68+
{
69+
// Retry on the GameThread.
70+
TWeakObjectPtr<UAsyncTaskThirdwebLoginWithSiwe> WeakThis = this;
71+
FFunctionGraphTask::CreateAndDispatchWhenReady([WeakThis, Error]()
72+
{
73+
if (WeakThis.IsValid())
74+
{
75+
WeakThis->HandleFailed(Error);
76+
}
77+
},
78+
TStatId(),
79+
nullptr,
80+
ENamedThreads::GameThread);
81+
}
82+
}

Source/Thirdweb/Private/Browser/ThirdwebOAuthBrowserUserWidget.cpp

Lines changed: 53 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22

33
#include "Browser/ThirdwebOAuthBrowserUserWidget.h"
44

5-
#include "ThirdwebLog.h"
6-
#include "ThirdwebRuntimeSettings.h"
5+
#include "Async/TaskGraphInterfaces.h"
76
#include "Blueprint/WidgetTree.h"
87
#include "Browser/ThirdwebOAuthExternalBrowser.h"
98
#include "Components/Overlay.h"
109
#include "Components/OverlaySlot.h"
1110
#include "Components/PanelWidget.h"
11+
#include "ThirdwebLog.h"
12+
#include "ThirdwebRuntimeSettings.h"
1213

1314
#if PLATFORM_ANDROID
1415
#include "Android/AndroidApplication.h"
@@ -70,7 +71,7 @@ void UThirdwebOAuthBrowserUserWidget::BeginDestroy()
7071
FString UThirdwebOAuthBrowserUserWidget::GetDummyUrl()
7172
{
7273
#if PLATFORM_ANDROID
73-
return UThirdwebRuntimeSettings::GetAppUri();
74+
return UThirdwebRuntimeSettings::GetAppUri();
7475
#else
7576
return DummyUrl;
7677
#endif
@@ -88,8 +89,8 @@ void UThirdwebOAuthBrowserUserWidget::Authenticate(const FInAppWalletHandle& InA
8889
TW_LOG(VeryVerbose, TEXT("OAuthBrowserUserWidget::Authenticate::Wallet Type::%s"), Wallet.GetSourceString());
8990
if (Wallet == FInAppWalletHandle::Siwe)
9091
{
91-
TW_LOG(VeryVerbose, TEXT("OAuthBrowserUserWidget::Authenticate::Authenticating against SIWE"));
92-
ExternalBrowser->Authenticate(TEXT("SIWE"));
92+
TW_LOG(VeryVerbose, TEXT("OAuthBrowserUserWidget::Authenticate::" "Authenticating against %s"), Wallet.GetSourceString());
93+
ExternalBrowser->Authenticate(Wallet.GetSourceString());
9394
return;
9495
}
9596

@@ -102,17 +103,21 @@ void UThirdwebOAuthBrowserUserWidget::Authenticate(const FInAppWalletHandle& InA
102103
TW_LOG(VeryVerbose, TEXT("OAuthBrowserUserWidget::Authenticate::Authenticating against %s"), *Link);
103104

104105
#if PLATFORM_ANDROID
105-
if (JNIEnv *Env = FAndroidApplication::GetJavaEnv())
106-
{
107-
jstring JUrl = Env->NewStringUTF(TCHAR_TO_UTF8(*Link));
108-
jclass JClass = FAndroidApplication::FindJavaClass("com/thirdweb/unrealengine/ThirdwebActivity");
109-
static jmethodID JLaunchUrl = FJavaWrapper::FindStaticMethod(Env, JClass, "startActivity", "(Landroid/app/Activity;Ljava/lang/String;)V", false);
110-
ThirdwebUtils::Internal::Android::CallJniStaticVoidMethod(Env, JClass, JLaunchUrl, FJavaWrapper::GameActivityThis, JUrl);
111-
TW_LOG(Verbose, TEXT("OAuthBrowserUserWidget::Authenticate::Opening CustomTabs"));
112-
return;
113-
}
114-
TW_LOG(Error, TEXT("OAuthBrowserUserWidget::Authenticate::No JNIEnv found"));
115-
return;
106+
if (JNIEnv *Env = FAndroidApplication::GetJavaEnv()) {
107+
jstring JUrl = Env->NewStringUTF(TCHAR_TO_UTF8(*Link));
108+
jclass JClass = FAndroidApplication::FindJavaClass(
109+
"com/thirdweb/unrealengine/ThirdwebActivity");
110+
static jmethodID JLaunchUrl = FJavaWrapper::FindStaticMethod(
111+
Env, JClass, "startActivity",
112+
"(Landroid/app/Activity;Ljava/lang/String;)V", false);
113+
ThirdwebUtils::Internal::Android::CallJniStaticVoidMethod(
114+
Env, JClass, JLaunchUrl, FJavaWrapper::GameActivityThis, JUrl);
115+
TW_LOG(Verbose,
116+
TEXT("OAuthBrowserUserWidget::Authenticate::Opening CustomTabs"));
117+
return;
118+
}
119+
TW_LOG(Error, TEXT("OAuthBrowserUserWidget::Authenticate::No JNIEnv found"));
120+
return;
116121
#endif
117122

118123
return ExternalBrowser->Authenticate(Link);
@@ -156,9 +161,27 @@ void UThirdwebOAuthBrowserUserWidget::HandleAuthenticated(const FString& AuthRes
156161
OnAuthenticated.Broadcast(AuthResult);
157162
}
158163

159-
void UThirdwebOAuthBrowserUserWidget::HandleSiweComplete(const FString& Signature, const FString& Payload)
164+
void UThirdwebOAuthBrowserUserWidget::HandleSiweComplete(const FString& Payload, const FString& Signature)
160165
{
161-
OnSiweComplete.Broadcast(Signature, Payload);
166+
if (IsInGameThread())
167+
{
168+
OnSiweComplete.Broadcast(Payload, Signature);
169+
}
170+
else
171+
{
172+
// Dispatch to game thread
173+
TWeakObjectPtr<UThirdwebOAuthBrowserUserWidget> WeakThis = this;
174+
FFunctionGraphTask::CreateAndDispatchWhenReady([WeakThis, Payload, Signature]()
175+
{
176+
if (WeakThis.IsValid())
177+
{
178+
WeakThis->OnSiweComplete.Broadcast(Payload, Signature);
179+
}
180+
},
181+
TStatId(),
182+
nullptr,
183+
ENamedThreads::GameThread);
184+
}
162185
}
163186

164187
void UThirdwebOAuthBrowserUserWidget::HandleError(const FString& Error)
@@ -167,16 +190,18 @@ void UThirdwebOAuthBrowserUserWidget::HandleError(const FString& Error)
167190
}
168191

169192
#if PLATFORM_ANDROID
170-
void UThirdwebOAuthBrowserUserWidget::HandleDeepLink(const FString &Url)
171-
{
172-
TW_LOG(VeryVerbose, TEXT("UThirdwebOAuthBrowserUserWidget::HandleDeepLink::%s"), *Url);
173-
HandleUrlChanged(Url);
193+
void UThirdwebOAuthBrowserUserWidget::HandleDeepLink(const FString &Url) {
194+
TW_LOG(VeryVerbose,
195+
TEXT("UThirdwebOAuthBrowserUserWidget::HandleDeepLink::%s"), *Url);
196+
HandleUrlChanged(Url);
174197
}
175198

176-
void UThirdwebOAuthBrowserUserWidget::HandleCustomTabsDismissed(const FString &Url)
177-
{
178-
TW_LOG(VeryVerbose, TEXT("UThirdwebOAuthBrowserUserWidget::HandleCustomTabsDismissed::%s"), *Url);
179-
HandleUrlChanged(Url);
199+
void UThirdwebOAuthBrowserUserWidget::HandleCustomTabsDismissed(
200+
const FString &Url) {
201+
TW_LOG(VeryVerbose,
202+
TEXT("UThirdwebOAuthBrowserUserWidget::HandleCustomTabsDismissed::%s"),
203+
*Url);
204+
HandleUrlChanged(Url);
180205
}
181206
#endif
182207

@@ -188,7 +213,7 @@ void UThirdwebOAuthBrowserUserWidget::SetVisible(const bool bVisible)
188213
if (bCollapseWhenBlank)
189214
{
190215
#if PLATFORM_IOS | PLATFORM_ANDROID
191-
SetRenderOpacity(1.0f);
216+
SetRenderOpacity(1.0f);
192217
#else
193218
SetVisibility(ESlateVisibility::Visible);
194219
#endif
@@ -200,7 +225,7 @@ void UThirdwebOAuthBrowserUserWidget::SetVisible(const bool bVisible)
200225
if (bCollapseWhenBlank)
201226
{
202227
#if PLATFORM_IOS | PLATFORM_ANDROID
203-
SetRenderOpacity(0.01f);
228+
SetRenderOpacity(0.01f);
204229
#else
205230
SetVisibility(ESlateVisibility::Collapsed);
206231
#endif

0 commit comments

Comments
 (0)