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.
  • Loading branch information
CassiusPacheco committed Sep 27, 2023
1 parent 4badf93 commit 1626b03
Show file tree
Hide file tree
Showing 11 changed files with 149 additions and 8 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,9 @@ namespace Immutable.Passport.Core

public interface IBrowserCommunicationsManager
{
public event OnUnityPostMessageDelegate? OnAuthPostMessage;
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 +36,7 @@ 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;

/// <summary>
/// Timeout time for waiting for each call to respond in milliseconds
Expand All @@ -45,6 +48,7 @@ public BrowserCommunicationsManager(IWebBrowserClient webBrowserClient)
{
this.webBrowserClient = webBrowserClient;
this.webBrowserClient.OnUnityPostMessage += OnUnityPostMessage;
this.webBrowserClient.OnAuthPostMessage += onUnityPostMessage;
}

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

#endregion

#region Browser to Unity
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)
{
Debug.Log($"{TAG} onUnityPostMessage: {message}");
OnAuthPostMessage?.Invoke(message);
}

private void HandleResponse(string message)
{
Debug.Log($"{TAG} HandleResponse message: " + message);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ 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;
InitRequest request = new() { ClientId = clientId, Environment = environment, RedirectUri = redirectUri };

string response = await communicationsManager.Call(
Expand Down Expand Up @@ -87,6 +88,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 +109,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
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,6 +11,7 @@ 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 GreeBrowserClient()
{
Expand All @@ -24,7 +25,8 @@ public GreeBrowserClient()
err: (msg) =>
{
Debug.LogError($"{TAG} err: {msg}");
}
},
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 +44,17 @@ private void _cb(string msg)
InvokeOnUnityPostMessage(msg);
}

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

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

internal void InvokeOnUnityPostMessage(string message)
{
OnUnityPostMessage?.Invoke(message);
Expand All @@ -51,5 +64,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 @@ -48,6 +48,7 @@ public class Singleton
public Callback onJS;
public Callback onError;
public Callback onHttpError;
public Callback onAuth;

public static Singleton Instance
{
Expand All @@ -69,6 +70,7 @@ public class WebViewObject
Callback onJS;
Callback onError;
Callback onHttpError;
Callback onAuth;
#if UNITY_ANDROID
class AndroidCallback : AndroidJavaProxy
{
Expand Down Expand Up @@ -101,6 +103,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 @@ -155,6 +159,22 @@ private static void delegateMessageReceived(string key, string message) {
return;
}

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

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

Debug.Log($"{TAG} delegateMessageReceived unsupported key " + key);
}
#endif
Expand Down Expand Up @@ -182,6 +202,7 @@ public void Init(
Callback cb = null,
Callback err = null,
Callback 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 +211,7 @@ public void Init(
onJS = cb;
onError = err;
onHttpError = httpErr;
onAuth = auth;
#if UNITY_WEBGL
#if !UNITY_EDITOR
_gree_unity_webview_init();
Expand All @@ -204,6 +226,7 @@ public void Init(
Singleton.Instance.onJS = ((message) => CallFromJS(message));
Singleton.Instance.onError = ((message) => CallOnError(message));
Singleton.Instance.onHttpError = ((message) => CallOnHttpError(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,6 +281,17 @@ public void EvaluateJS(string js)
#endif
}

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 error)
{
if (onError != null)
Expand All @@ -274,6 +308,14 @@ public void CallOnHttpError(string error)
}
}

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

public void CallFromJS(string message)
{
if (onJS != null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

#import <UIKit/UIKit.h>
#import <WebKit/WebKit.h>
#import <AuthenticationServices/AuthenticationServices.h>

// NOTE: we need extern without "C" before unity 4.5
extern "C" UIViewController *UnityGetGLViewController();
Expand Down Expand Up @@ -86,7 +87,7 @@ - (void)load:(NSURLRequest *)request
}
@end

@interface CWebViewPlugin : NSObject<WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler>
@interface CWebViewPlugin : NSObject<WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler, ASWebAuthenticationPresentationContextProviding>
{
WKWebView *webView;
}
Expand All @@ -97,6 +98,7 @@ @implementation CWebViewPlugin
static WKProcessPool *_sharedProcessPool;
static NSMutableArray *_instances = [[NSMutableArray alloc] init];
static CWebViewPlugin *__delegate = nil;
static ASWebAuthenticationSession *_authSession;

- (id)initWithUa:(const char *)ua
{
Expand Down Expand Up @@ -343,6 +345,30 @@ - (void)evaluateJS:(const char *)js
NSString *jsStr = [NSString stringWithUTF8String:js];
[webView evaluateJavaScript:jsStr completionHandler:^(NSString *result, NSError *error) {}];
}

- (void)launchAuthURL:(const char *)url
{
NSURL *URL = [[NSURL alloc] initWithString: [NSString stringWithUTF8String:url]];
NSString *scheme = NSBundle.mainBundle.bundleIdentifier;

_authSession = [[ASWebAuthenticationSession alloc] initWithURL:URL callbackURLScheme:scheme completionHandler:^(NSURL * _Nullable callbackURL, NSError * _Nullable error) {
_authSession = nil;

if (error != nil && error.code != ASAuthorizationErrorCanceled) {
[self sendUnityCallback:"CallFromAuthCallbackError" message:error.localizedDescription.UTF8String];
} else {
[self sendUnityCallback:"CallFromAuthCallback" message: callbackURL != nil ? callbackURL.absoluteString.UTF8String : ""];
}
}];

_authSession.presentationContextProvider = self;
[_authSession start];
}

- (ASPresentationAnchor)presentationAnchorForWebAuthenticationSession:(ASWebAuthenticationSession *)session
{
return UIApplication.sharedApplication.windows.firstObject;
}
@end

extern "C" {
Expand All @@ -351,6 +377,7 @@ - (void)evaluateJS:(const char *)js
void _CWebViewPlugin_LoadURL(void *instance, const char *url);
void _CWebViewPlugin_EvaluateJS(void *instance, const char *url);
void _CWebViewPlugin_SetDelegate(DelegateCallbackFunction callback);
void _CWebViewPlugin_LaunchAuthURL(void *instance, const char *url);
}

void _CWebViewPlugin_SetDelegate(DelegateCallbackFunction callback) {
Expand Down Expand Up @@ -389,3 +416,11 @@ void _CWebViewPlugin_EvaluateJS(void *instance, const char *js)
CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance;
[webViewPlugin evaluateJS:js];
}

void _CWebViewPlugin_LaunchAuthURL(void *instance, const char *url)
{
if (instance == NULL)
return;
CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance;
[webViewPlugin launchAuthURL:url];
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ namespace Immutable.Browser.Core
public interface IWebBrowserClient
{
event OnUnityPostMessageDelegate OnUnityPostMessage;
event OnUnityPostMessageDelegate OnAuthPostMessage;

void ExecuteJs(string js);

void LaunchAuthURL(string url);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,7 @@ internal void InvokeLoadProgressChange(double progress)
/// Invoked when the browser goes in or out of fullscreen
/// </summary>
public event OnFullscreenChange OnFullscreen;
public event OnUnityPostMessageDelegate OnAuthPostMessage;

internal void InvokeFullscreen(bool fullscreen)
{
Expand Down Expand Up @@ -737,6 +738,11 @@ public void ExecuteJs(string js)
communicationsManager.ExecuteJs(js);
}

public void LaunchAuthURL(string url)
{
Application.OpenURL(url);
}

[DebuggerStepThrough]
private void CheckIfIsReadyAndConnected()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ public void CallAndResponse_Success_BrowserReady()
internal class MockBrowserClient : IWebBrowserClient
{
public event OnUnityPostMessageDelegate? OnUnityPostMessage;
public event OnUnityPostMessageDelegate? OnAuthPostMessage;

public BrowserRequest? request = null;
public BrowserResponse? browserResponse = null;
public bool setRequestId = true;
Expand Down Expand Up @@ -191,5 +193,10 @@ private string Between(string value, string a, string b)
}
return value[adjustedPosA..posB];
}

public void LaunchAuthURL(string url)
{
throw new NotImplementedException();
}
}
}
Loading

0 comments on commit 1626b03

Please sign in to comment.