Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: use ASWebAuthenticationSession for iOS PKCE #88

Merged
merged 2 commits into from
Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -35,6 +38,13 @@ public class BrowserCommunicationsManager : IBrowserCommunicationsManager
private readonly IWebBrowserClient webBrowserClient;
public event OnBrowserReadyDelegate? OnReady;

/// <summary>
/// PKCE in some platforms such as iOS and macOS will not trigger a deeplink and a proper callback needs to be
/// setup.
/// </summary>
public event OnUnityPostMessageDelegate? OnAuthPostMessage;
public event OnUnityPostMessageErrorDelegate? OnPostMessageError;

/// <summary>
/// Timeout time for waiting for each call to respond in milliseconds
/// Default value: 1 minute
Expand All @@ -44,7 +54,9 @@ public class BrowserCommunicationsManager : IBrowserCommunicationsManager
public BrowserCommunicationsManager(IWebBrowserClient webBrowserClient)
{
this.webBrowserClient = webBrowserClient;
this.webBrowserClient.OnUnityPostMessage += OnUnityPostMessage;
this.webBrowserClient.OnUnityPostMessage += InvokeOnUnityPostMessage;
this.webBrowserClient.OnAuthPostMessage += InvokeOnAuthPostMessage;
this.webBrowserClient.OnPostMessageError += InvokeOnPostMessageError;
}

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

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

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

#endregion

#region Browser to Unity

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

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

private void HandleResponse(string message)
Expand Down
26 changes: 25 additions & 1 deletion src/Packages/Passport/Runtime/Scripts/Private/PassportImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,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;

var versionInfo = new VersionInfo
{
Expand Down Expand Up @@ -102,6 +104,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 Down Expand Up @@ -129,7 +132,7 @@ private async UniTask LaunchAuthUrl()
AndroidJavaClass customTabLauncher = new AndroidJavaClass("com.immutable.unity.ImmutableAndroid");
customTabLauncher.CallStatic("launchUrl", activity, url);
#else
Application.OpenURL(url);
communicationsManager.LaunchAuthURL(url);
#endif
return;
}
Expand Down Expand Up @@ -362,5 +365,26 @@ 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;
}
}
}
}
6 changes: 3 additions & 3 deletions src/Packages/Passport/Runtime/Scripts/Public/Passport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ private Passport()
#if UNITY_EDITOR_WIN
Application.quitting += OnQuit;
#elif UNITY_IPHONE || UNITY_ANDROID || UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX
Application.deepLinkActivated += onDeepLinkActivated;
Application.deepLinkActivated += OnDeepLinkActivated;
if (!string.IsNullOrEmpty(Application.absoluteURL))
{
// Cold start and Application.absoluteURL not null so process Deep Link.
onDeepLinkActivated(Application.absoluteURL);
OnDeepLinkActivated(Application.absoluteURL);
}
#endif
}
Expand Down 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}");
}
cb: InvokeOnUnityPostMessage,
httpErr: InvokeOnPostMessageError,
err: InvokeOnPostMessageError,
auth: InvokeOnAuthPostMessage
);
#if UNITY_ANDROID
string filePath = Constants.SCHEME_FILE + ANDROID_DATA_DIRECTORY + Constants.PASSPORT_DATA_DIRECTORY_NAME + Constants.PASSPORT_HTML_FILE_NAME;
Expand All @@ -36,10 +33,15 @@ public GreeBrowserClient()
webViewObject.LoadURL(filePath);
}

private void _cb(string msg)
private void InvokeOnPostMessageError(string id, string message)
{
Debug.Log($"Received call from browser: {msg}");
InvokeOnUnityPostMessage(msg);
Debug.LogError($"{TAG} id: {id} err: {message}");
OnPostMessageError.Invoke(id, message);
}

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

internal void InvokeOnUnityPostMessage(string message)
Expand All @@ -51,5 +53,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
[DllImport("WebView")]
Expand Down Expand Up @@ -139,23 +144,29 @@ 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;
}

Debug.Log($"{TAG} delegateMessageReceived unsupported key " + key);
if (key == "CallFromAuthCallback") {
if (Singleton.Instance.onAuth != null) {
Debug.Log($"{TAG} ==== CallFromAuthCallback callback running message: " + message);
Singleton.Instance.onAuth(message);
}
return;
}
}
#endif

Expand All @@ -170,18 +181,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 +202,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 +215,9 @@ public void Init(
#elif UNITY_IPHONE || UNITY_STANDALONE_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 +272,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
Loading