SFSafariViewController and oAuth flows

Google (and others) require that oAuth login flows go through an external browser (to avoid phishing scams etc.)

“In the coming months, we will no longer allow OAuth requests to Google in embedded browsers known as “web-views”, such as the WebView UI element on Android and UIWebView/WKWebView on iOS, and equivalents on Windows and OS X.”

However, Apple is now rejecting binary updates that open an external browser. They REQUIRE using the Safari View Controller API (SFSafariViewController).

I’m surprised that this isn’t biting everybody right now - maybe they’re slowly starting to squeeze or maybe not that many people are dealing with oauth or maybe google isn’t squeezing all requests yet?

In any case - due to this requirement, is it something that Unity can provide? I didn’t see anything on the Asset Store yet.

2 Likes

Alternatively, it seems like writing a plugin to swap Application.OpenURL() for this wouldn’t be too much code to write… especially if url schemes work to get back to the app

Does anyone have a snippet of objective-c to pop into the plugins folder that would handle this?

Here’s a reference: iOS 9: Getting Started With SFSafariViewController | Envato Tuts+ (scroll down to #5)

this works on ios to open SFSafariViewController with a given url (put into Plugins/ios/SafariView.mm)

#import <SafariServices/SafariServices.h>

extern UIViewController * UnityGetGLViewController();
extern void UnitySendMessage( const char * className, const char * methodName, const char * param );

extern "C"
{
  @interface SafariViewController : UIViewController<SFSafariViewControllerDelegate>
  // ...
  @end

  @implementation SafariViewController
  - (void)safariViewControllerDidFinish:(SFSafariViewController *)controller {
    NSLog(@"safariViewControllerDidFinish");
    UnitySendMessage("YourSignInGameObject", "OnAuthCompleted", "");
  }
  @end

  SafariViewController * svc;

  void launchUrl(const char * url)
  {
    NSLog(@"Launching SFSafariViewController");

    // Get the instance of ViewController that Unity is displaying now
    UIViewController * uvc = UnityGetGLViewController();

    // Generate an NSURL object based on the C string passed from C#
    NSURL * URL = [NSURL URLWithString: [[NSString alloc] initWithUTF8String:url]];

    // Create an SFSafariViewController object from the generated URL
    SFSafariViewController * sfvc = [[SFSafariViewController alloc] initWithURL:URL];

    // Assign a delegate to handle when the user presses the 'Done' button
    svc = [[SafariViewController alloc] init];
    sfvc.delegate = svc;

    // Start the generated SFSafariViewController object
    [uvc presentViewController:sfvc animated:YES completion:nil];

    NSLog(@"Presented SFSafariViewController");
  }

  void dismiss()
  {
    UIViewController * uvc = UnityGetGLViewController();
    [uvc dismissViewControllerAnimated:YES completion:nil];
  }
}

Then C# wrapper to call it:

using System.Runtime.InteropServices;
using UnityEngine;

public static class SFSafariView
{
#if UNITY_IOS
    [DllImport("__Internal")]
    extern static void launchUrl(string url);
    [DllImport("__Internal")]
    extern static void dismiss();
#endif

    public static void LaunchUrl(string url)
    {
#if UNITY_IOS
        launchUrl(url);
#endif
    }

    public static void Dismiss()
    {
#if UNITY_IOS
        dismiss();
#endif
    }
}

Remember also to select SafariView.mm in your Unity project, then in the Inspector expand Rarely used services and tick SafariServices to ensure this compiles.

Note the usage of UnitySendMessage within the SFSafariViewControllerDelegate class implementation to notify your C# script that the user pressed the Done button.

2 Likes

I followed your instructions and I tried to test in the editor, however it gave me this
EntryPointNotFoundException: launchUrl

It is normal (it can’t be tested in Editor) or I’m doing something wrong?

1 Like

I have been successful in getting custom url to launch the app from safari app post facebook authentication.
Now I am trying to get it to work with SFSafariViewController. The custom url call does not make any difference at all.

  1. How do I get this working? I am very new to ios development. Am I missing anything?
  2. Alternatively, in this line below, I want to send the uri received from facebook to my gameobject
UnitySendMessage("YourSignInGameObject", "OnAuthCompleted", uri);

How do I access this uri in SFSafariViewController?

Any help would be greatly appreciated.

I had the same problem, and I found that the EntryPointNotFoundException get raised when you try to run the code under Unity Editor (Windows or MacOSX, it’s the same).

You need to be sure to run it exclusively when you’re in UNITY_IOS mode, so on a “real” IOS Device.

I implemented this and it works perfectly on an iOS device. Thanks a lot for a clean and easy solution. What I now need is a callback in Unity once the web page has finished loading by the SFSafariView controller.

Below is what I found, but I am not sure how I can implement it.
safariViewController(_:didCompleteInitialLoad:)
https://developer.apple.com/documentation/safariservices/sfsafariviewcontrollerdelegate/1621215-safariviewcontroller

Any help would be greatly appreciated.

Edit :
I found a solution for myself, just add below lines of code to SafariView.mm :

@implementation SafariViewController

  • (void)safariViewController:(SFSafariViewController *)controller didCompleteInitialLoad:(BOOL)didLoadSuccessfully{
    NSLog(@“safariViewController didCompleteInitialLoad”);
    UnitySendMessage(“YourUnityGameObjectName”, “MethodName”, “AnyString”);
    }
    @

Replace smiles.:slight_smile: and :frowning: with colon and open/close round brackets. :stuck_out_tongue:

1 Like

Here is one option to solve this - Web/SSO Authentication for Unity [iOS/macOS/iPadOS] | Network | Unity Asset Store

How to do it for unity-Mac application, any solution?