diff --git a/Content/Example.umap b/Content/Example.umap index 5dc9570..6187f74 100644 Binary files a/Content/Example.umap and b/Content/Example.umap differ diff --git a/Plugins/Web3AuthSDK/Content/AuthInterface.uasset b/Plugins/Web3AuthSDK/Content/AuthInterface.uasset index ec18126..f64c6fc 100644 Binary files a/Plugins/Web3AuthSDK/Content/AuthInterface.uasset and b/Plugins/Web3AuthSDK/Content/AuthInterface.uasset differ diff --git a/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Private/Web3Auth.cpp b/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Private/Web3Auth.cpp index 7929195..4a7548e 100644 --- a/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Private/Web3Auth.cpp +++ b/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Private/Web3Auth.cpp @@ -46,12 +46,30 @@ void UWeb3Auth::CallJniVoidMethod(JNIEnv* Env, const jclass Class, jmethodID Met } #endif +UWeb3Auth::UWeb3Auth(): crypto(nullptr), keyStoreUtils(nullptr) +{ + bIsRequestResponse = false; +} + +FSignResponse UWeb3Auth::signResponse; + void UWeb3Auth::setOptions(FWeb3AuthOptions options) { this->web3AuthOptions = options; authorizeSession(); } -void UWeb3Auth::request(FString path, FLoginParams* loginParams = nullptr, TSharedPtr extraParams = nullptr) { +FSignResponse UWeb3Auth::getSignResponse() +{ + //UE_LOG(LogTemp, Log, TEXT("signResponse: %s"), *signResponse.ToString()); + return signResponse; +} + +void UWeb3Auth::setSignResponse(const FSignResponse response) +{ + signResponse = response; +} + +void UWeb3Auth::processRequest(FString path, FLoginParams* loginParams = nullptr, TSharedPtr extraParams = nullptr) { TSharedPtr paramMap = MakeShareable(new FJsonObject); @@ -333,7 +351,7 @@ void UWeb3Auth::launchWalletServices(FChainConfig chainConfig) { void UWeb3Auth::processLogin(FLoginParams loginParams) { UE_LOG(LogTemp, Warning, TEXT("login called")); - this->request("login", &loginParams); + this->processRequest("login", &loginParams); } void UWeb3Auth::processLogout() { @@ -344,19 +362,237 @@ void UWeb3Auth::enableMFA(FLoginParams loginParams) { UE_LOG(LogTemp, Warning, TEXT("enableMFA called")); this->sessionId = keyStoreUtils->Get(); if (!this->sessionId.IsEmpty()) { - this->request("enable_mfa", &loginParams); + this->processRequest("enable_mfa", &loginParams); } else { UE_LOG(LogTemp, Error, TEXT("SessionId not found. Please login first.")); } } +void UWeb3Auth::request(FChainConfig chainConfig, FString method, TArray requestParams, FString path) +{ + this->sessionId = keyStoreUtils->Get(); + if (!this->sessionId.IsEmpty()) + { + TSharedPtr paramMap = MakeShareable(new FJsonObject); + + TSharedPtr initParams = MakeShareable(new FJsonObject); + initParams->SetStringField("clientId", web3AuthOptions.clientId); + + switch (web3AuthOptions.network) { + case FNetwork::MAINNET: + initParams->SetStringField("network", "mainnet"); + break; + case FNetwork::TESTNET: + initParams->SetStringField("network", "testnet"); + break; + case FNetwork::CYAN: + initParams->SetStringField("network", "cyan"); + break; + case FNetwork::AQUA: + initParams->SetStringField("network", "aqua"); + break; + case FNetwork::SAPPHIRE_DEVNET: + initParams->SetStringField("network", "sapphire_devnet"); + break; + case FNetwork::SAPPHIRE_MAINNET: + initParams->SetStringField("network", "sapphire_mainnet"); + break; + } + + FMfaSettings defaultMFA; + + if (!(web3AuthOptions.mfaSettings == defaultMFA)) { + FString mfaSettingsJson; + FJsonObjectConverter::UStructToJsonObjectString(web3AuthOptions.mfaSettings, + mfaSettingsJson); + initParams->SetStringField(TEXT("mfaSettings"), mfaSettingsJson); + } + + if (web3AuthOptions.sessionTime > 0) { + initParams->SetNumberField(TEXT("sessionTime"), web3AuthOptions.sessionTime); + } + +#if !PLATFORM_ANDROID && !PLATFORM_IOS + FString redirectUrl = startLocalWebServer(); + initParams->SetStringField("redirectUrl", redirectUrl); +#else + if (web3AuthOptions.redirectUrl != "") + initParams->SetStringField("redirectUrl", web3AuthOptions.redirectUrl); +#endif + + switch (web3AuthOptions.buildEnv) { + case FBuildEnv::PRODUCTION: + initParams->SetStringField("buildEnv", "production"); + break; + case FBuildEnv::TESTING: + initParams->SetStringField("buildEnv", "testing"); + break; + case FBuildEnv::STAGING: + initParams->SetStringField("buildEnv", "staging"); + break; + } + + if (web3AuthOptions.whiteLabel.appName != "") { + FString output; + FJsonObjectConverter::UStructToJsonObjectString(FWhiteLabelData::StaticStruct(), + &web3AuthOptions.whiteLabel, output); + + initParams->SetStringField("whiteLabel", output); + } + + FString chainConfigOutput; + FJsonObjectConverter::UStructToJsonObjectString(FChainConfig::StaticStruct(), &chainConfig, chainConfigOutput); + initParams->SetStringField("chainConfig", chainConfigOutput); + + if (!web3AuthOptions.loginConfig.IsEmpty()) { + FString output; + + TSharedPtr loginConfigMap = MakeShareable(new FJsonObject); + + for (auto item: web3AuthOptions.loginConfig) { + TSharedPtr loginConfigObject = MakeShareable(new FJsonObject); + FJsonObjectConverter::UStructToJsonObject(FLoginConfigItem::StaticStruct(), + &item.Value, + loginConfigObject.ToSharedRef(), 0, 0); + + loginConfigMap->SetObjectField(item.Key, loginConfigObject); + } + + TSharedRef > Writer = TJsonWriterFactory<>::Create(&output); + FJsonSerializer::Serialize(loginConfigMap.ToSharedRef(), Writer); + + initParams->SetStringField("loginConfig", output); + } + + paramMap->SetObjectField("options", initParams.ToSharedRef()); + //paramMap->SetStringField("actionType", "login"); + + FString json; + TSharedRef > jsonWriter = TJsonWriterFactory<>::Create(&json); + FJsonSerializer::Serialize(paramMap.ToSharedRef(), jsonWriter); + + if (web3AuthOptions.buildEnv == FBuildEnv::STAGING) { + web3AuthOptions.walletSdkUrl = "https://staging-wallet.web3auth.io/v2"; + } else if (web3AuthOptions.buildEnv == FBuildEnv::TESTING) { + web3AuthOptions.walletSdkUrl = "https://develop-wallet.web3auth.io"; + } else { + web3AuthOptions.walletSdkUrl = "https://wallet.web3auth.io/v2"; + } + + //createSession(json, 86400, true); + FString newSessionKey = crypto->generateRandomSessionKey(); + FString ephemPublicKey = crypto->generatePublicKey(newSessionKey); + FString ivKey = crypto->generateRandomBytes(16); + + FString macKeyHex = FString(); + FString encryptedData = crypto->encrypt(json, newSessionKey, ephemPublicKey, ivKey, macKeyHex); + + FString finalMac = crypto->getMac(encryptedData, ephemPublicKey, ivKey, macKeyHex); + + if (!crypto->hmacSha256Verify(macKeyHex, crypto->getCombinedData(encryptedData, ephemPublicKey, ivKey), finalMac)) + { + // throw new BadMacException + FString errorMessage = TEXT("Bad MAC error during encryption"); + } + + FShareMetaData shareMetaData; + shareMetaData.ciphertext = encryptedData; + shareMetaData.ephemPublicKey = ephemPublicKey; + shareMetaData.iv = ivKey; + shareMetaData.mac = finalMac; + + TSharedPtr jsonObject = MakeShareable(new FJsonObject); + FJsonObjectConverter::UStructToJsonObject(FShareMetaData::StaticStruct(), &shareMetaData, jsonObject.ToSharedRef(), 0, 0); + + FString jsonString; + TSharedRef> json_writer = TJsonWriterFactory<>::Create(&jsonString); + FJsonSerializer::Serialize(jsonObject.ToSharedRef(), json_writer); + + FString sig = crypto->generateECDSASignature(newSessionKey, jsonString); + + FLogoutApiRequest request; + request.data = jsonString; + request.key = ephemPublicKey; + request.signature = sig; + request.timeout = FMath::Min(86400, 7 * 86400); + + web3AuthApi->CreateSession(request, [this, newSessionKey, method, requestParams, path](FString response) + { + UE_LOG(LogTemp, Log, TEXT("Response: %s"), *response); + + TSharedPtr requestData = MakeShared(); + requestData->SetStringField(TEXT("method"), method); + + // Serialize requestParams into a JSON array + TArray> jsonArray; + for (const FString& param : requestParams) + { + jsonArray.Add(MakeShared(param)); + } + + // Convert the JSON array to a string + FString paramsString; + TSharedRef> writer = TJsonWriterFactory<>::Create(¶msString); + FJsonSerializer::Serialize(jsonArray, writer); + + // Add the serialized JSON array string to requestData + requestData->SetStringField(TEXT("params"), paramsString); + + // Create the JSON object for signMessageMap + TSharedPtr signMessageMap = MakeShared(); + signMessageMap->SetStringField(TEXT("loginId"), newSessionKey); + signMessageMap->SetStringField(TEXT("sessionId"), this->sessionId); + signMessageMap->SetStringField(TEXT("platform"), TEXT("unreal")); + signMessageMap->SetObjectField(TEXT("request"), requestData); + + // Print the JSON string for debugging + FString outputString; + TSharedRef> outputWriter1 = TJsonWriterFactory<>::Create(&outputString); + FJsonSerializer::Serialize(signMessageMap.ToSharedRef(), outputWriter1); + //UE_LOG(LogTemp, Log, TEXT("Final JSON: %s"), *outputString); + + // Convert to Base64 + FString output; + TSharedRef< TJsonWriter<> > outputWriter = TJsonWriterFactory<>::Create(&output); + FJsonSerializer::Serialize(signMessageMap.ToSharedRef(), outputWriter); + FString encode = FBase64::Encode(output); + + // Build the URI + FString url = web3AuthOptions.walletSdkUrl + "/" + path + "#" + "b64Params=" + encode; + bIsRequestResponse = true; + + #if PLATFORM_ANDROID + thiz_instance = this; + if (JNIEnv* Env = FAndroidApplication::GetJavaEnv(true)) + { + jstring jurl = Env->NewStringUTF(TCHAR_TO_UTF8(*url)); + + jclass jbrowserViewClass = FAndroidApplication::FindJavaClass("com/Plugins/Web3AuthSDK/BrowserView"); + static jmethodID jlaunchUrl = FJavaWrapper::FindStaticMethod(Env, jbrowserViewClass, "launchUrl", "(Landroid/app/Activity;Ljava/lang/String;)V", false); + + CallJniVoidMethod(Env, jbrowserViewClass, jlaunchUrl, FJavaWrapper::GameActivityThis, jurl); + } + #elif PLATFORM_IOS + thiz_instance = this; + [[WebAuthenticate Singleton] launchUrl:TCHAR_TO_ANSI(*url)]; + #else + FPlatformProcess::LaunchURL(*url, nullptr, nullptr); + #endif + }); + } + else + { + UE_LOG(LogTemp, Error, TEXT("SessionId not found. Please login first.")); + } +} + void UWeb3Auth::setResultUrl(FString hash) { if (hash.IsEmpty()) { return; } - //UE_LOG(LogTemp, Warning, TEXT("respose base64 %s"), *hash); + //UE_LOG(LogTemp, Warning, TEXT("response base64 %s"), *hash); TArray decodedBytes; FBase64::Decode(hash, decodedBytes); @@ -366,7 +602,30 @@ void UWeb3Auth::setResultUrl(FString hash) { int32 braceIndex = decodedString.Find(TEXT("}")); FString substringBeforeBrace = decodedString.Left(braceIndex + 1); //UE_LOG(LogTemp, Warning, TEXT("substringBeforeBrace: %s"), *substringBeforeBrace); - + + if(bIsRequestResponse) { + try + { + TSharedPtr jsonObject; + TSharedRef> reader = TJsonReaderFactory<>::Create(substringBeforeBrace); + if (FJsonSerializer::Deserialize(reader, jsonObject) && jsonObject.IsValid()) + { + signResponse.success = jsonObject->GetBoolField(TEXT("success")); + signResponse.result = jsonObject->GetStringField(TEXT("result")); + signResponse.error = jsonObject->GetStringField(TEXT("error")); + UE_LOG(LogTemp, Warning, TEXT("signResponse - success: %d, result: %s, error: %s"), signResponse.success, *signResponse.result, *signResponse.error); + setSignResponse(signResponse); + } + } + catch (const std::exception& ex) + { + UE_LOG(LogTemp, Warning, TEXT("Failed to parse SignResponse JSON")); + } + bIsRequestResponse = false; + return; + } + + FSessionResponse response; TSharedPtr jsonObject; TSharedRef> reader = TJsonReaderFactory<>::Create(substringBeforeBrace); if (FJsonSerializer::Deserialize(reader, jsonObject) && jsonObject.IsValid()) @@ -567,9 +826,9 @@ void UWeb3Auth::authorizeSession() { if (web3AuthResponse.error != "") { return; } - + (void) this->loginEvent.ExecuteIfBound(web3AuthResponse); - (void) this->mfaEvent.ExecuteIfBound(true); + (void) this->mfaEvent.ExecuteIfBound(true); } }); @@ -693,6 +952,7 @@ void UWeb3Auth::handleCreateSessionResponse(const FString& path, const FString& } else { url = web3AuthOptions.sdkUrl + "/" + path + "#" + "b64Params=" + encode; } + bIsRequestResponse = false; #if PLATFORM_ANDROID thiz_instance = this; diff --git a/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Public/Web3Auth.h b/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Public/Web3Auth.h index 4f28361..dd7b1ed 100644 --- a/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Public/Web3Auth.h +++ b/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Public/Web3Auth.h @@ -3,34 +3,28 @@ #include "CoreMinimal.h" #include "GameFramework/Actor.h" -#include "Json.h" #include "JsonUtilities.h" -#include "Misc/Base64.h" #include "ECCrypto.h" -#include "Web3AuthApi.h" #include "KeyStoreUtils.h" +#include "Web3AuthApi.h" +#include "Misc/Base64.h" +#include "Dom/JsonValue.h" #include "Containers/UnrealString.h" -#include "Serialization/JsonSerializer.h" #include "Serialization/JsonReader.h" +#include "Serialization/JsonSerializer.h" -#include "Runtime/Online/HTTPServer/Public/HttpPath.h" #include "Runtime/Online/HTTPServer/Public/IHttpRouter.h" -#include "Runtime/Online/HTTPServer/Public/HttpServerHttpVersion.h" -#include "Runtime/Online/HTTPServer/Public/HttpServerModule.h" -#include "Runtime/Online/HTTPServer/Public/HttpServerResponse.h" - +#include "HttpServerModule.h" #if PLATFORM_ANDROID #include "../../../Launch/Public/Android/AndroidJNI.h" #include "Android/AndroidApplication.h" #endif -#include "Web3AuthError.h" #include "Web3Auth.generated.h" - UENUM(BlueprintType) enum class FDisplay : uint8 { @@ -478,6 +472,10 @@ struct FUserInfo && oAuthAccessToken.IsEmpty(); } + FString ToString() const { + return FString::Printf(TEXT("email: %s, name: %s, profileImage: %s, aggregateVerifier: %s, verifier: %s, verifierId: %s, typeOfLogin: %s, dappShare: %s, idToken: %s, oAuthIdToken: %s, oAuthAccessToken: %s, isMfaEnabled: %d"), + *email, *name, *profileImage, *aggregateVerifier, *verifier, *verifierId, *typeOfLogin, *dappShare, *idToken, *oAuthIdToken, *oAuthAccessToken, isMfaEnabled); + } }; USTRUCT(BlueprintType) @@ -793,6 +791,8 @@ class WEB3AUTHSDK_API UWeb3Auth : public UGameInstanceSubsystem virtual void Initialize(FSubsystemCollectionBase& Collection) override; virtual void Deinitialize() override; public: + UWeb3Auth(); + UPROPERTY(VisibleAnywhere, BlueprintReadOnly) FWeb3AuthResponse web3AuthResponse; @@ -808,12 +808,15 @@ class WEB3AUTHSDK_API UWeb3Auth : public UGameInstanceSubsystem UFUNCTION(BlueprintCallable) void processLogout(); - UFUNCTION(BlueprintCallable) + UFUNCTION(BlueprintCallable) void enableMFA(FLoginParams loginParams); UFUNCTION(BlueprintCallable) void launchWalletServices(FChainConfig chainConfig); + UFUNCTION(BlueprintCallable) + void request(FChainConfig chainConfig, FString method, TArray requestParams, FString path = "wallet/request"); + UFUNCTION(BlueprintCallable) void setResultUrl(FString code); @@ -843,12 +846,21 @@ class WEB3AUTHSDK_API UWeb3Auth : public UGameInstanceSubsystem UFUNCTION(BlueprintCallable) FUserInfo getUserInfo(); + UFUNCTION(BlueprintCallable) + static void setSignResponse(const FSignResponse Response); + + UFUNCTION(BlueprintPure) + static FSignResponse getSignResponse(); + #if PLATFORM_IOS static void callBackFromWebAuthenticateIOS(NSString* sResult); #endif private: - void request(FString path, FLoginParams* loginParams, TSharedPtr extraParam); - + void processRequest(FString path, FLoginParams* loginParams, TSharedPtr extraParam); + + bool bIsRequestResponse; + static FSignResponse signResponse; + template void GetJsonStringFromStruct(StructType FilledStruct, FString& StringOutput); diff --git a/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Public/Web3AuthApi.h b/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Public/Web3AuthApi.h index 4075d87..81a4816 100644 --- a/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Public/Web3AuthApi.h +++ b/Plugins/Web3AuthSDK/Source/Web3AuthSDK/Public/Web3AuthApi.h @@ -45,6 +45,29 @@ struct FSessionResponse FString sessionId; }; +USTRUCT(BlueprintType) +struct FSignResponse +{ + GENERATED_BODY() + +public: + UPROPERTY() + bool success; + + UPROPERTY() + FString result; + + UPROPERTY() + FString error; + + FSignResponse() {}; + + FString ToString() const { + return FString::Printf(TEXT("success: %s, result: %s, error: %s"), + success ? TEXT("true") : TEXT("false"), *result, *error); + } +}; + UCLASS() class WEB3AUTHSDK_API UWeb3AuthApi : public UObject { diff --git a/Source/Web3AuthUnrealSDK/Web3AuthUnrealSDK.Build.cs b/Source/Web3AuthUnrealSDK/Web3AuthUnrealSDK.Build.cs index c2c27b1..48e977d 100644 --- a/Source/Web3AuthUnrealSDK/Web3AuthUnrealSDK.Build.cs +++ b/Source/Web3AuthUnrealSDK/Web3AuthUnrealSDK.Build.cs @@ -8,7 +8,7 @@ public Web3AuthUnrealSDK(ReadOnlyTargetRules Target) : base(Target) { PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; - PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" }); + PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "Json", "JsonUtilities" }); PrivateDependencyModuleNames.AddRange(new string[] { });