Skip to content

Commit

Permalink
feat: use ASWebAuthenticationSession for iOS PKCE
Browse files Browse the repository at this point in the history
This is the standard way of dealing with SSO on iOS.

Asynchronous error handling is now being bubbled up to PassportImpl
where it can be parsed by their identifier to complete unfinished
tasks.
  • Loading branch information
CassiusPacheco committed Sep 29, 2023
1 parent 4badf93 commit 2648059
Show file tree
Hide file tree
Showing 12 changed files with 209 additions and 33 deletions.
6 changes: 5 additions & 1 deletion src/Packages/Passport/Editor/PassportPostprocess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,11 @@ public void OnPostprocessBuild(BuildReport report)
var method = type.GetMethod("AddFrameworkToProject");
method.Invoke(proj, new object[] { target, "WebKit.framework", false });
}

{
var method = type.GetMethod("AddFrameworkToProject");
method.Invoke(proj, new object[] { target, "AuthenticationServices.framework", false });
}

var cflags = "";
if (EditorUserBuildSettings.development)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ namespace Immutable.Passport.Core

public interface IBrowserCommunicationsManager
{
public event OnUnityPostMessageDelegate? OnAuthPostMessage;
public event OnUnityPostMessageErrorDelegate? OnPostMessageError;
public void SetCallTimeout(int ms);
public void LaunchAuthURL(string url);
public UniTask<string> Call(string fxName, string? data = null, bool ignoreTimeout = false);
}

Expand All @@ -34,6 +37,8 @@ public class BrowserCommunicationsManager : IBrowserCommunicationsManager
private readonly IDictionary<string, UniTaskCompletionSource<string>> requestTaskMap = new Dictionary<string, UniTaskCompletionSource<string>>();
private readonly IWebBrowserClient webBrowserClient;
public event OnBrowserReadyDelegate? OnReady;
public event OnUnityPostMessageDelegate? OnAuthPostMessage;
public event OnUnityPostMessageErrorDelegate? OnPostMessageError;

/// <summary>
/// Timeout time for waiting for each call to respond in milliseconds
Expand All @@ -44,7 +49,9 @@ public class BrowserCommunicationsManager : IBrowserCommunicationsManager
public BrowserCommunicationsManager(IWebBrowserClient webBrowserClient)
{
this.webBrowserClient = webBrowserClient;
this.webBrowserClient.OnUnityPostMessage += OnUnityPostMessage;
this.webBrowserClient.OnUnityPostMessage += onUnityPostMessage;
this.webBrowserClient.OnAuthPostMessage += onAuthPostMessage;
this.webBrowserClient.OnPostMessageError += onPostMessageError;
}

#region Unity to Browser
Expand Down Expand Up @@ -84,14 +91,32 @@ private void CallFunction(string requestId, string fxName, string? data = null)
webBrowserClient.ExecuteJs(js);
}

public void LaunchAuthURL(string url)
{
Debug.Log($"{TAG} LaunchAuthURL");
webBrowserClient.LaunchAuthURL(url);
}

private void onUnityPostMessage(string message)
{
Debug.Log($"{TAG} onUnityPostMessage: {message}");
HandleResponse(message);
}

#endregion

#region Browser to Unity

private void OnUnityPostMessage(string message)
private void onAuthPostMessage(string message)
{
Debug.Log($"{TAG} OnUnityPostMessage: {message}");
HandleResponse(message);
Debug.Log($"{TAG} onAuthPostMessage: {message}");
OnAuthPostMessage?.Invoke(message);
}

private void onPostMessageError(string id, string message)
{
Debug.Log($"{TAG} onPostMessageError id: {id} message: {message}");
OnPostMessageError?.Invoke(id, message);
}

private void HandleResponse(string message)
Expand Down
28 changes: 27 additions & 1 deletion src/Packages/Passport/Runtime/Scripts/Private/PassportImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ public PassportImpl(IBrowserCommunicationsManager communicationsManager)
public async UniTask Init(string clientId, string environment, string? redirectUri = null, string? deeplink = null)
{
this.redirectUri = redirectUri;
this.communicationsManager.OnAuthPostMessage += OnDeepLinkActivated;
this.communicationsManager.OnPostMessageError += onPostMessageError;
InitRequest request = new() { ClientId = clientId, Environment = environment, RedirectUri = redirectUri };

string response = await communicationsManager.Call(
Expand Down Expand Up @@ -87,6 +89,7 @@ public async UniTask Connect(long? timeoutMs = null)

public async void OnDeepLinkActivated(string url)
{
Debug.Log($"{TAG} OnDeepLinkActivated: {url} starts with {redirectUri}");
if (url.StartsWith(redirectUri))
await CompletePKCEFlow(url);
}
Expand All @@ -107,7 +110,7 @@ private async UniTask LaunchAuthUrl()

if (response?.Success == true && response?.Result != null)
{
Application.OpenURL(response.Result.Replace(" ", "+"));
communicationsManager.LaunchAuthURL(response.Result.Replace(" ", "+"));
return;
}
else
Expand Down Expand Up @@ -339,5 +342,28 @@ public async UniTask<string> ZkEvmGetBalance(string address, string blockNumberO
string callResponse = await communicationsManager.Call(PassportFunction.ZK_EVM.GET_BALANCE, json);
return JsonConvert.DeserializeObject<StringResponse>(callResponse).Result ?? "0x0";
}

private void onPostMessageError(string id, string message)
{
if (id == "CallFromAuthCallbackError")
{
if (message == "")
{
Debug.Log($"{TAG} Get PKCE Auth URL user cancelled");
pkceCompletionSource.TrySetCanceled();
}
else
{
Debug.Log($"{TAG} Get PKCE Auth URL error: {message}");
pkceCompletionSource.TrySetException(new PassportException(
"Something went wrong, please call ConnectPKCE() again",
PassportErrorType.AUTHENTICATION_ERROR
));
}
return;
}

Debug.Log($"{TAG} Unhandled onPostMessageError. id: {id} message: {message}");
}
}
}
2 changes: 1 addition & 1 deletion src/Packages/Passport/Runtime/Scripts/Public/Passport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ private PassportImpl GetPassportImpl()
throw new PassportException("Passport not initialised");
}

private async void onDeepLinkActivated(string url)
private void onDeepLinkActivated(string url)
{
deeplink = url;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,17 @@ public class GreeBrowserClient : IWebBrowserClient
private const string MAC_DATA_DIRECTORY = "/Resources/Data";
private readonly WebViewObject webViewObject;
public event OnUnityPostMessageDelegate OnUnityPostMessage;
public event OnUnityPostMessageDelegate OnAuthPostMessage;
public event OnUnityPostMessageErrorDelegate OnPostMessageError;

public GreeBrowserClient()
{
webViewObject = new();
webViewObject.Init(
cb: _cb,
httpErr: (msg) =>
{
Debug.LogError($"{TAG} http err: {msg}");
},
err: (msg) =>
{
Debug.LogError($"{TAG} err: {msg}");
}
httpErr: _onPostMessageError,
err: _onPostMessageError,
auth: _onAuthPostMessage
);
#if UNITY_ANDROID
string filePath = Constants.SCHEME_FILE + ANDROID_DATA_DIRECTORY + Constants.PASSPORT_DATA_DIRECTORY_NAME + Constants.PASSPORT_HTML_FILE_NAME;
Expand All @@ -42,6 +39,23 @@ private void _cb(string msg)
InvokeOnUnityPostMessage(msg);
}

private void _onAuthPostMessage(string url)
{
Debug.Log($"Received auth url: {url}");
InvokeOnAuthPostMessage(url);
}

private void _onPostMessageError(string id, string message)
{
Debug.LogError($"{TAG} id: {id} err: {message}");
OnPostMessageError.Invoke(id, message);
}

internal void InvokeOnAuthPostMessage(string message)
{
OnAuthPostMessage.Invoke(message);
}

internal void InvokeOnUnityPostMessage(string message)
{
OnUnityPostMessage?.Invoke(message);
Expand All @@ -51,5 +65,10 @@ public void ExecuteJs(string js)
{
webViewObject.EvaluateJS(js);
}

public void LaunchAuthURL(string url)
{
webViewObject.LaunchAuthURL(url);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,16 @@
#endif

using Callback = System.Action<string>;
using ErrorCallback = System.Action<string, string>;

#if UNITY_IPHONE || UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX
public class Singleton
{
private static Singleton _instance;
public Callback onJS;
public Callback onError;
public Callback onHttpError;
public ErrorCallback onError;
public ErrorCallback onHttpError;
public Callback onAuth;

public static Singleton Instance
{
Expand All @@ -67,8 +69,9 @@ public class WebViewObject
{
private const string TAG = "[WebViewObject]";
Callback onJS;
Callback onError;
Callback onHttpError;
ErrorCallback onError;
ErrorCallback onHttpError;
Callback onAuth;
#if UNITY_ANDROID
class AndroidCallback : AndroidJavaProxy
{
Expand Down Expand Up @@ -101,6 +104,8 @@ private static extern void _CWebViewPlugin_LoadURL(
private static extern void _CWebViewPlugin_EvaluateJS(
IntPtr instance, string url);
[DllImport("__Internal")]
private static extern void _CWebViewPlugin_LaunchAuthURL(IntPtr instance, string url);
[DllImport("__Internal")]
private static extern void _CWebViewPlugin_SetDelegate(DelegateMessage callback);
#elif UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX
[DllImport("WebView")]
Expand Down Expand Up @@ -139,18 +144,26 @@ private static void delegateMessageReceived(string key, string message) {
return;
}

if (key == "CallOnError") {
if (key == "CallOnError" || key == "CallFromAuthCallbackError") {
if (Singleton.Instance.onError != null) {
Debug.Log($"{TAG} ==== onError callback running message: " + message);
Singleton.Instance.onError(message);
Singleton.Instance.onError(key, message);
}
return;
}

if (key == "CallOnHttpError") {
if (Singleton.Instance.onHttpError != null) {
Debug.Log($"{TAG} ==== onHttpError callback running message: " + message);
Singleton.Instance.onHttpError(message);
Singleton.Instance.onHttpError(key, message);
}
return;
}

if (key == "CallFromAuthCallback") {
if (Singleton.Instance.onAuth != null) {
Debug.Log($"{TAG} ==== CallFromAuthCallback callback running message: " + message);
Singleton.Instance.onAuth(message);
}
return;
}
Expand All @@ -170,18 +183,19 @@ public void handleMessage(string message)
CallFromJS(message.Substring(i + 1));
break;
case "CallOnError":
CallOnError(message.Substring(i + 1));
CallOnError("CallOnError", message.Substring(i + 1));
break;
case "CallOnHttpError":
CallOnHttpError(message.Substring(i + 1));
CallOnHttpError("CallOnHttpError", message.Substring(i + 1));
break;
}
}

public void Init(
Callback cb = null,
Callback err = null,
Callback httpErr = null,
ErrorCallback err = null,
ErrorCallback httpErr = null,
Callback auth = null,
string ua = "",
// android
int androidForceDarkMode = 0 // 0: follow system setting, 1: force dark off, 2: force dark on
Expand All @@ -190,6 +204,7 @@ public void Init(
onJS = cb;
onError = err;
onHttpError = httpErr;
onAuth = auth;
#if UNITY_WEBGL
#if !UNITY_EDITOR
_gree_unity_webview_init();
Expand All @@ -202,8 +217,9 @@ public void Init(
#elif UNITY_IPHONE || UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX
webView = _CWebViewPlugin_Init(ua);
Singleton.Instance.onJS = ((message) => CallFromJS(message));
Singleton.Instance.onError = ((message) => CallOnError(message));
Singleton.Instance.onHttpError = ((message) => CallOnHttpError(message));
Singleton.Instance.onError = ((id, message) => CallOnError(id, message));
Singleton.Instance.onHttpError = ((id, message) => CallOnHttpError(id, message));
Singleton.Instance.onAuth = ((message) => CallOnAuth(message));
_CWebViewPlugin_SetDelegate(delegateMessageReceived);
#elif UNITY_ANDROID
webView = new AndroidJavaObject("net.gree.unitywebview.CWebViewPluginNoUi");
Expand Down Expand Up @@ -258,19 +274,38 @@ public void EvaluateJS(string js)
#endif
}

public void CallOnError(string error)
public void LaunchAuthURL(string url)
{
#if UNITY_IPHONE
if (webView == IntPtr.Zero)
return;
_CWebViewPlugin_LaunchAuthURL(webView, url);
#else
Application.OpenURL(url);
#endif
}

public void CallOnError(string id, string error)
{
if (onError != null)
{
onError(error);
onError(id, error);
}
}

public void CallOnHttpError(string error)
public void CallOnHttpError(string id, string error)
{
if (onHttpError != null)
{
onHttpError(error);
onHttpError(id, error);
}
}

public void CallOnAuth(string url)
{
if (onAuth != null)
{
onAuth(url);
}
}

Expand Down
Loading

0 comments on commit 2648059

Please sign in to comment.