Utiliser SFSafariViewController pour se connecter en Oauth simplement avec #Xamarin sur #iOS
La connexion à des services OAuth est quelque chose de très classique dans une application mobile. Dans cet article nous verrons comment utiliser le composant SFSafariViewController afin de bénéficier de la meilleure expérience utilisateur possible.
Pourquoi utiliser le SFSafariViewController ?
Ce composant apparu avec iOS9 permet d'afficher Safari sous la forme d'une popup : rassurant pour l'utilisateur car il voit une interface classique ainsi que la barre d'adresse lui indiquant où il se trouve.
Cela permet aussi de bénéficier des cookies et du cache de l'utilisateur : s'il est déjà connecté à Facebook dans Safari il n'aura pas à le refaire par exemple.
Aussi, ce composant est considéré comme plus sécurisé d'un point de vue technique et permet de s'authentifier avec des services l'interdisant autrement (coucou Google).
Voici un exemple de screen :
Quelques limitations
Il existe quelques limitations à connaître lors de l'utilisation de ce composant :
- Il n'est pas possible de s'abonner aux changements d'URL.
- Il n'est pas possible d'injecter de scripts JS.
- Il n'est pas possible de vider le cache - il faut demander à l'utilisateur d'aller au fin fond des paramètres d'iOS pour le faire lui-même.
Ouverture d'une URL
L'ouverture d'une URL est très simple : on instancie un SFSafariViewController en lui fournissant l'URL à charger et on demande au ViewController courant de l'afficher.
_sfViewController = new SFSafariViewController(new NSUrl(authUrl)); PresentViewController(_sfViewController, animated: true, completionHandler: null );
Masquer le SFSafariViewController
Par défaut l'utilisateur peut fermer le controller lui-même en cliquant sur le bouton "Ok" mais il est aussi possible de le faire via le code en réaction à un événement extérieur :
_sfViewController?.DismissViewController( animated: true, completionHandler: null);
Se connecter en OAuth à un service
Pour résumer, la connexion à un service Oauth présente une mire de connexion à l'utilisateur dans une page HTML et lors d'une connexion et autorisation réussie, le site web vous redirige vers une URL donnée. Vous configurez généralement cette URL lors de l'inscription de votre application sur ce service tiers.
Le problème recontré ici vient du fait que vous ne pouvez pas détecter cette redirection vers l'URL finale.
Pour contourner ce problème, nous allons implémenter un protocole personnalisé dans notre application et demander au service tiers de nous rediriger vers celui-ci. Ainsi cela activera l'application à nouveau et nous pourrons lire le code OAuth fourni et fermer le SFSafariViewController.
La première étape consiste à ajouter un protocole personnalisé. Je vais pour cela aller modifier le fichier info.plist et ajouter une entrée CFBundleURLTypes. La procédure est décrite dans la documentation Apple. Ici mon application pourra être lancée via une URL du type LeNomDuProtocole://kikou=lol :
<key>CFBundleURLTypes</key> <array> <dict> <key>CFBundleTypeRole</key> <string>Editor</string> <key>CFBundleURLName</key> <string>com.jonathanantoine.TVST.iOS</string> <key>CFBundleURLSchemes</key> <array> <string>jonathanantoineTVSTiOS</string> </array> </dict> </array>
La seconde étape consiste à détecter l'appel de ce protocole personnalisé. Pour cela on surcharge (override) la méthode HandleOpenURL de notre classe UIApplicationDelegate. On utilisera alors la classe NSNotificationCenter (une sorte de Mediator/Messenger dans une app iOS) pour envoyer un message indiquant l'ouverture via un protocole personnalisé.
public override bool HandleOpenURL(UIApplication application, NSUrl url) { NSNotificationCenter.DefaultCenter .PostNotificationName("oauthLoggin:", url); return true; }
La dernière étape consiste à s'abonner à ce message et d'aller lire le code fourni par le service tiers. On veillera aussi à se placer sur le bon Thread pour fermer le SFSafariViewController.
NSNotificationCenter.DefaultCenter.AddObserver( new NSString("oauthLoggin:"), (param) => { var url = (param.Object as NSUrl).AbsoluteString; var code = // lecture du code if (!string.IsNullOrEmpty(code)) { DispatchQueue.MainQueue.DispatchAsync( () => { _sfViewController? .DismissViewController(animated: true, completionHandler: null); }); AuthentDoneAsync(code); } } } });
Comment permettre de se déconnecter
Un soucis récurrent dans ce contexte est la déconnexion : comme l'utilisateur est connecté et ses cookies conservés, le service redirige souvent directement vers votre application sans laisser une chance d'utiliser un autre compte. L'astuce à connaître et de demander une première fois l'affichage de l'URL de déconnexion du service - si elle est disponible - avant d'afficher à nouveau l'URL de connexion. Par chance, on peut détecter le premier affichage correct du SFSafariViewController. La manière la plus simple est la suivante :
- Faire dériver votre ViewController courant de l'interface ISFSafariViewControllerDelegate.
- Implémenter le protocole iOS safariViewController:didCompleteInitialLoad:
- Spécifier votre ViewController courant comme Delegate du SFSafariViewController avant de l'afficher.
public void StartLogOut(){ var logoutUrl = "urlDeLogout"; _sfViewController = new SFSafariViewController(new NSUrl(logoutUrl)); _sfViewController.Delegate = this; PresentViewController(_sfViewController, true, null); } [Foundation.Export("safariViewController:didCompleteInitialLoad:")] public void DidCompleteInitialLoad(SFSafariViewController c, bool done) { if (done) { // chargement terminé avec succès } else { // erreur de chargement :'( } }
Et voici le workflow final complet avec d'abord un click sur le bouton ok puis une connection complètte jusqu'au bout :
Commentaires