Search Unity

SFSafariViewController and oAuth flows

Discussion in 'iOS and tvOS' started by dmko, Jul 20, 2017.

  1. dmko

    dmko

    Joined:
    Jun 15, 2014
    Posts:
    65
    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."

    - https://developers.googleblog.com/2016/08/modernizing-oauth-interactions-in-native-apps.html

    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.
     
    pandolfini and ina like this.
  2. dmko

    dmko

    Joined:
    Jun 15, 2014
    Posts:
    65
  3. faxedhead

    faxedhead

    Joined:
    Dec 21, 2014
    Posts:
    3
    this works on ios to open SFSafariViewController with a given url (put into Plugins/ios/SafariView.mm)

    Code (CSharp):
    1.  
    2. #import <SafariServices/SafariServices.h>
    3.  
    4. extern UIViewController * UnityGetGLViewController();
    5. extern void UnitySendMessage( const char * className, const char * methodName, const char * param );
    6.  
    7. extern "C"
    8. {
    9.   @interface SafariViewController : UIViewController<SFSafariViewControllerDelegate>
    10.   // ...
    11.   @end
    12.  
    13.   @implementation SafariViewController
    14.   - (void)safariViewControllerDidFinish:(SFSafariViewController *)controller {
    15.     NSLog(@"safariViewControllerDidFinish");
    16.     UnitySendMessage("YourSignInGameObject", "OnAuthCompleted", "");
    17.   }
    18.   @end
    19.  
    20.   SafariViewController * svc;
    21.  
    22.   void launchUrl(const char * url)
    23.   {
    24.     NSLog(@"Launching SFSafariViewController");
    25.  
    26.     // Get the instance of ViewController that Unity is displaying now
    27.     UIViewController * uvc = UnityGetGLViewController();
    28.  
    29.     // Generate an NSURL object based on the C string passed from C#
    30.     NSURL * URL = [NSURL URLWithString: [[NSString alloc] initWithUTF8String:url]];
    31.  
    32.     // Create an SFSafariViewController object from the generated URL
    33.     SFSafariViewController * sfvc = [[SFSafariViewController alloc] initWithURL:URL];
    34.  
    35.     // Assign a delegate to handle when the user presses the 'Done' button
    36.     svc = [[SafariViewController alloc] init];
    37.     sfvc.delegate = svc;
    38.  
    39.     // Start the generated SFSafariViewController object
    40.     [uvc presentViewController:sfvc animated:YES completion:nil];
    41.  
    42.     NSLog(@"Presented SFSafariViewController");
    43.   }
    44.  
    45.   void dismiss()
    46.   {
    47.     UIViewController * uvc = UnityGetGLViewController();
    48.     [uvc dismissViewControllerAnimated:YES completion:nil];
    49.   }
    50. }
    51.  
    52.  
    Then C# wrapper to call it:

    Code (CSharp):
    1. using System.Runtime.InteropServices;
    2. using UnityEngine;
    3.  
    4. public static class SFSafariView
    5. {
    6. #if UNITY_IOS
    7.     [DllImport("__Internal")]
    8.     extern static void launchUrl(string url);
    9.     [DllImport("__Internal")]
    10.     extern static void dismiss();
    11. #endif
    12.  
    13.     public static void LaunchUrl(string url)
    14.     {
    15. #if UNITY_IOS
    16.         launchUrl(url);
    17. #endif
    18.     }
    19.  
    20.     public static void Dismiss()
    21.     {
    22. #if UNITY_IOS
    23.         dismiss();
    24. #endif
    25.     }
    26. }
    27.  
    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.
     
    Last edited: Jun 22, 2018
    Northmaen and ltomov like this.
  4. TiagoAlvesPretia

    TiagoAlvesPretia

    Joined:
    Oct 4, 2017
    Posts:
    5
    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?
     
    BitSiesta likes this.
  5. aqvin

    aqvin

    Joined:
    Jul 16, 2018
    Posts:
    1
    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
    Code (CSharp):
    1. UnitySendMessage("YourSignInGameObject", "OnAuthCompleted", uri);
    How do I access this uri in SFSafariViewController?

    Any help would be greatly appreciated.
     
  6. BitSiesta

    BitSiesta

    Joined:
    Nov 26, 2018
    Posts:
    1
    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.
     
  7. Prathamesh_Prasade

    Prathamesh_Prasade

    Joined:
    Dec 5, 2017
    Posts:
    10
    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/documen...ntrollerdelegate/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");
    }
    @End

    Replace smiles.:) and :( with colon and open/close round brackets. :p
     
    Last edited: Oct 10, 2019
    ina likes this.
  8. alexeydonald

    alexeydonald

    Joined:
    Sep 12, 2013
    Posts:
    7
  9. Anand-Biradar

    Anand-Biradar

    Joined:
    Jun 29, 2018
    Posts:
    1
    How to do it for unity-Mac application, any solution?