web-dev-qa-db-fra.com

SafariViewController: Comment récupérer le jeton OAuth à partir de l'URL?

Essayer d'utiliser Facebook OAuth avec SafariViewController. D'abord j'ouvre l'authURL avec SafariViewController, qui si l'utilisateur est connecté à Facebook sur Safari, les redirigera et renverra un OAuth URL avec le jeton pour ce service spécifique, par exemple Instagram

RÉPONSE: https://www.facebook.com/connect/login_success.html#access_token=BLAHTOKENRESPONSE&expires_in=5114338

Lorsque SafariViewController a redirigé je veux récupérer l'URL de réponse et la stocker afin de pouvoir récupérer le jeton. Voici mon code:

import SafariServices

let kSafariViewControllerCloseNotification = "kSafariViewControllerCloseNotification"

import UIKit

// facebook OAuth URL for service
let authURL = NSURL(string: "https://www.facebook.com/dialog/oauth?client_id=3627644767&redirect_uri=https://www.facebook.com/connect/login_success.html&scope=basic_info,email,public_profile,user_about_me,user_activities,user_birthday,user_education_history,user_friends,user_interests,user_likes,user_location,user_photos,user_relationship_details&response_type=token")

class ViewController: UIViewController, SFSafariViewControllerDelegate {

    var safariVC: SFSafariViewController?
    @IBOutlet weak var loginButton: UIButton!

    @IBAction func loginButtonTapped(sender: UIButton) {
        safariVC = SFSafariViewController(URL: authURL!)
        safariVC!.delegate = self
        self.presentViewController(safariVC!, animated: true, completion: nil)
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // not firing the safariLogin function below
        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ViewController.safariLogin(_:)), name: kSafariViewControllerCloseNotification, object: nil)
    }

    func safariLogin(notification: NSNotification) {
        print("Safari Login call")
        // get the url form the auth callback
        let url = notification.object as! NSURL
        print(url)
        self.safariVC!.dismissViewControllerAnimated(true, completion: nil)
    }

    func application(application: UIApplication, openURL url: NSURL, sourceApplication: String?, annotation: AnyObject) -> Bool {
        print("application call")
        // just making sure we send the notification when the URL is opened in SFSafariViewController
        if (sourceApplication == "com.application.SafariViewTest") {
            NSNotificationCenter.defaultCenter().postNotificationName(kSafariViewControllerCloseNotification, object: url)
            return true
        }
        return true
    }  

}

Il ouvre l'authURL et redirige vers l'URL de réponse correcte, mais l'observateur ne déclenche pas la fonction safariLogin pour récupérer l'URL. Toute aide serait très appréciée!

Merci beaucoup!

18
Brien Crean

iOS 12

iOS 12 Beta déprécie déjà SFAuthenticationSession (voir ci-dessous) au profit de ASWebAuthenticationSession . Il semble qu'il soit utilisé exactement de la même manière mais nécessite le nouveau framework AuthenticationServices .

iOS 11

iOS 11 a introduit SFAuthenticationSession qui est tellement plus facile à gérer. Compte tenu de sa nature, cette API bêta peut encore changer, mais il existe déjà quelques exemples ( 1 , 2 ) sur Internet. Tout d'abord, vous avez besoin d'un gestionnaire d'achèvement qui est appelé avec le résultat de la demande d'authentification:

let completion : SFAuthenticationSession.CompletionHandler = { (callBack:URL?, error:Error?) in
    guard error == nil, let successURL = callBack else {
        return
    }

    let oauthToken = NSURLComponents(string: (successURL.absoluteString))?.queryItems?.filter({$0.name == "oauth_token"}).first

    // Do what you have to do...
}

Il vous suffit ensuite de créer une session SFAuthenticationSession et de la démarrer.

let authURL = "https://the.service.you/want/toAuthorizeWith?..."
let scheme = "YOURSCHEME://"
let authSession = SFAuthenticationSession(url: authURL, callbackURLScheme: scheme, completionHandler: completion)
authSession.start()

iOS 10 et versions antérieures

Comme certains l'ont noté dans les commentaires, la réponse acceptée est incomplète et ne fonctionnera pas d'elle-même. Lorsque vous vous promenez dans le blog de Strawberry Code, vous pouvez trouver un lien vers le projet GitHub associé . Le README.MD De ce projet explique une partie cruciale de la configuration, à savoir l'ajout de l'URI de redirection à Info.plist. Donc, tout se passe comme suit:

AppDelegate.Swift

func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {

   if let sourceApplication = options[.sourceApplication] {
       if (String(describing: sourceApplication) == "com.Apple.SafariViewService") {
            NotificationCenter.default.post(name: Notification.Name("CallbackNotification"), object: url)
            return true
        }
    }

    return false
}

ViewController.Swift

Inscrivez-vous à la notification pour appeler votre gestionnaire dans un endroit raisonnable. Je recommande de ne pas le faire dans viewDidLoad() mais seulement avant de présenter réellement le SFSafariViewController.

NotificationCenter.default.addObserver(self, selector: #selector(safariLogin(_:)), name: Notification.Name("CallbackNotification"), object: nil)
let safariVC = SFSafariViewController(URL: authURL)
safariVC.delegate = self
self.present(safariVC, animated: true, completion: nil)

Et puis supprimez l'observance dans le gestionnaire:

@objc func safariLogin(_ notification : Notification) {

    NotificationCenter.default.removeObserver(self, name: Notification.Name("CallbackNotification"), object: nil)

    guard let url = notification.object as? URL else {
        return
    }

    // Parse url ...

}

N'oubliez pas que l'utilisateur peut ignorer le SFSafariViewController en appuyant sur le bouton Done, assurez-vous donc d'adopter le protocole SFSafariViewControllerDelegate et de supprimer également l'observation comme ceci:

func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
    NotificationCenter.default.removeObserver(self, name: Notification.Name("CallbackNotification"), object: nil)
}

Info.plist

Pour que tout fonctionne, vous devez ajouter votre schéma d'URI de redirection à Info.plist:

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLName</key>
        <string>com.YOUR.BUNDLE.IDENTIFIER</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>YOURSCHEME</string>
        </array>
    </dict>
</array>

Bien sûr, YOURSCHEME doit correspondre au schéma de l'URI de redirection que vous avez enregistré avec le service Web avec lequel vous essayez d'autoriser.

26
bfx