Tutoriel : Connecter des utilisateurs et appeler Microsoft Graph à partir d’une application iOS ou macOS
Dans ce tutoriel, vous allez créer une application iOS ou macOS qui s’intègre à la plateforme d’identités Microsoft afin de connecter des utilisateurs et obtenir un jeton d’accès pour appeler l’API Microsoft Graph.
À la fin de ce tutoriel, votre application accepte les connexions de comptes Microsoft personnels (y compris outlook.com ou live.com entre autres) et de comptes professionnels ou scolaires de toute entreprise ou organisation utilisant Microsoft Entra ID. Ce tutoriel est applicable aux applications iOS et macOS. Certaines étapes varient d’une plateforme à l’autre.
Dans ce tutoriel, vous allez :
- Créer un projet d’application iOS ou macOS dans Xcode.
- Inscrire l’application dans le centre d’administration Microsoft Entra
- Ajouter du code pour prendre en charge la connexion et la déconnexion des utilisateurs
- Ajouter du code pour appeler l’API Microsoft Graph.
- Test de l'application
Prérequis
Fonctionnement de l’application du tutoriel
L’application utilisée dans ce tutoriel peut connecter des utilisateurs et obtenir des données de Microsoft Graph en leur nom. Ces données sont accessibles par l’intermédiaire d’une API protégée (l’API Microsoft Graph dans ce cas précis) qui nécessite une autorisation et qui est protégée par la plateforme d’identité Microsoft.
Plus précisément :
- Votre application connecte l’utilisateur via un navigateur ou Microsoft Authenticator.
- L’utilisateur final accepte les autorisations que votre application a demandées.
- Votre application reçoit un jeton d’accès pour l’API Microsoft Graph.
- Le jeton d’accès est inclus dans la requête HTTP adressée à l’API web.
- La réponse Microsoft Graph est traitée.
Cet exemple utilise la bibliothèque d’authentification Microsoft (MSAL) pour implémenter l’authentification. MSAL renouvelle automatiquement les jetons, fournit une authentification unique entre les autres applications de l’appareil et gère les comptes.
Si vous souhaitez télécharger une version complète de l’application que vous avez générée dans ce tutoriel, vous trouverez les deux versions sur GitHub :
- Exemple de code iOS (GitHub)
- Exemple de code macOS (GitHub)
Création d'un projet
- Ouvrez Xcode et sélectionnez Create a new Xcode project (Créer un projet Xcode).
- Pour les applications iOS, sélectionnez iOS>Application avec affichage unique, puis Suivant.
- Pour les applications macOS, sélectionnez macOS>Application Cocoa, puis Suivant.
- Spécifiez un nom de produit.
- Choisissez Swift comme Langage, puis sélectionnez Suivant.
- Sélectionnez un dossier où créer votre application, puis sélectionnez Créer.
Enregistrement de l’application
Conseil
Les étapes décrites dans cet article peuvent varier légèrement en fonction du portail de départ.
- Connectez-vous au centre d’administration de Microsoft Entra au minimum en tant que Développeur d’application.
- Si vous avez accès à plusieurs tenants, utilisez l’icône Paramètres dans le menu supérieur pour basculer vers le tenant dans lequel vous voulez inscrire l’application à partir du menu Répertoires + abonnements.
- Accédez à Identité>Applications>Inscriptions d’applications.
- Sélectionnez Nouvelle inscription.
- Entrez un nom pour votre application. Les utilisateurs de votre application peuvent voir ce nom, et vous pouvez le changer ultérieurement.
- Sélectionnez Comptes dans un répertoire d’organisation (tout répertoire Microsoft Entra - Multilocataire) et comptes Microsoft personnels (par exemple, Skype, Xbox) sous Types de comptes pris en charge.
- Sélectionnez Inscrire.
- Sous Gérer, sélectionnez Authentification>Ajouter une plateforme>iOS/macOS.
- Entrez l’ID de bundle de votre projet. Si vous avez téléchargé l’exemple de code, l’ID de bundle est
com.microsoft.identitysample.MSALiOS
. Si vous créez votre propre projet, sélectionnez-le dans Xcode et ouvrez l’onglet Général. L’identificateur de bundle apparaît dans la section Identité. - Sélectionnez Configurer et enregistrez la Configuration MSAL qui apparaît dans la page Configuration MSAL pour pouvoir l’entrer plus tard quand vous devrez configurer votre application.
- Sélectionnez Terminé.
Ajouter MSAL
Choisissez l’une des méthodes suivantes pour installer la bibliothèque MSAL dans votre application :
CocoaPods
Si vous utilisez CocoaPods, installez
MSAL
en commençant par créer un fichier vide nommé podfile dans le même dossier que le fichier .xcodeproj de votre projet. Ajoutez l’élément suivant à podfile :use_frameworks! target '<your-target-here>' do pod 'MSAL' end
Remplacez
<your-target-here>
par le nom de votre projet.Dans une fenêtre de terminal, accédez au dossier qui contient le podfile que vous avez créé et exécutez
pod install
pour installer la bibliothèque MSAL.Fermez Xcode et ouvrez
<your project name>.xcworkspace
pour recharger le projet dans Xcode.
Carthage
Si vous utilisez Carthage, installez MSAL
en l’ajoutant à votre Cartfile :
github "AzureAD/microsoft-authentication-library-for-objc" "master"
À partir d’une fenêtre de terminal, dans le même répertoire que le Cartfile mis à jour, exécutez la commande suivante pour que Carthage mette à jour les dépendances dans votre projet.
iOS :
carthage update --platform iOS
macOS :
carthage update --platform macOS
Manuellement
Vous pouvez également utiliser Git Submodule ou consulter la dernière version pour l’utiliser comme framework dans votre application.
Ajouter l'enregistrement de l'application
Ensuite, nous ajoutons l’inscription de votre application à votre code.
Tout d’abord, ajoutez l’instruction import suivante en haut du fichier ViewController.swift et AppDelegate.swift ou SceneDelegate.swift :
import MSAL
Ensuite, ajoutez le code suivant à ViewController.swift avant viewDidLoad()
:
// Update the below to your client ID. The below is for running the demo only
let kClientID = "Your_Application_Id_Here"
let kGraphEndpoint = "https://graph.microsoft.com/" // the Microsoft Graph endpoint
let kAuthority = "https://login.microsoftonline.com/common" // this authority allows a personal Microsoft account and a work or school account in any organization's Azure AD tenant to sign in
let kScopes: [String] = ["user.read"] // request permission to read the profile of the signed-in user
var accessToken = String()
var applicationContext : MSALPublicClientApplication?
var webViewParameters : MSALWebviewParameters?
var currentAccount: MSALAccount?
La seule valeur que vous devez modifier est celle qui est attribuée à kClientID
comme ID d’application. Cette valeur fait partie des données de configuration MSAL que vous avez enregistrées lors de l'étape du début de ce didacticiel pour enregistrer l'application.
Configurer les paramètres d’un projet Xcode
Ajoutez un nouveau groupe de trousseaux à Signature et fonctionnalités pour votre projet. Le groupe de trousseaux doit être com.microsoft.adalcache
sur iOS et com.microsoft.identity.universalstorage
sur macOS.
Pour iOS uniquement, configurer des schémas d’URL
Lors de cette étape, vous allez inscrire CFBundleURLSchemes
afin que l’utilisateur puisse être redirigé vers l’application après la connexion. Notez que LSApplicationQueriesSchemes
permet également à votre application d’utiliser Microsoft Authenticator.
Dans Xcode, ouvrez Info.plist en tant que fichier de code source et ajoutez ce qui suit dans la section <dict>
. Remplacez [BUNDLE_ID]
par la valeur que vous avez utilisée précédemment. Si vous avez téléchargé le code, l’identificateur de bundle est com.microsoft.identitysample.MSALiOS
. Si vous créez votre propre projet, sélectionnez-le dans Xcode et ouvrez l’onglet Général. L’identificateur de bundle apparaît dans la section Identité.
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>msauth.[BUNDLE_ID]</string>
</array>
</dict>
</array>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>msauthv2</string>
<string>msauthv3</string>
</array>
Pour macOS uniquement, configurer App Sandbox
- Accédez à vos paramètres de projet Xcode >onglet Capacités>App Sandbox.
- Cochez la case Connexions sortantes (client) .
Créer l’interface utilisateur de votre application
À présent, créez une interface utilisateur qui comprend un bouton pour appeler l’API Microsoft Graph, un autre pour se déconnecter, et une vue texte pour afficher une sortie en ajoutant le code suivant à la classe ViewController
:
Interface utilisateur iOS
var loggingText: UITextView!
var signOutButton: UIButton!
var callGraphButton: UIButton!
var usernameLabel: UILabel!
func initUI() {
usernameLabel = UILabel()
usernameLabel.translatesAutoresizingMaskIntoConstraints = false
usernameLabel.text = ""
usernameLabel.textColor = .darkGray
usernameLabel.textAlignment = .right
self.view.addSubview(usernameLabel)
usernameLabel.topAnchor.constraint(equalTo: view.topAnchor, constant: 50.0).isActive = true
usernameLabel.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -10.0).isActive = true
usernameLabel.widthAnchor.constraint(equalToConstant: 300.0).isActive = true
usernameLabel.heightAnchor.constraint(equalToConstant: 50.0).isActive = true
// Add call Graph button
callGraphButton = UIButton()
callGraphButton.translatesAutoresizingMaskIntoConstraints = false
callGraphButton.setTitle("Call Microsoft Graph API", for: .normal)
callGraphButton.setTitleColor(.blue, for: .normal)
callGraphButton.addTarget(self, action: #selector(callGraphAPI(_:)), for: .touchUpInside)
self.view.addSubview(callGraphButton)
callGraphButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
callGraphButton.topAnchor.constraint(equalTo: view.topAnchor, constant: 120.0).isActive = true
callGraphButton.widthAnchor.constraint(equalToConstant: 300.0).isActive = true
callGraphButton.heightAnchor.constraint(equalToConstant: 50.0).isActive = true
// Add sign out button
signOutButton = UIButton()
signOutButton.translatesAutoresizingMaskIntoConstraints = false
signOutButton.setTitle("Sign Out", for: .normal)
signOutButton.setTitleColor(.blue, for: .normal)
signOutButton.setTitleColor(.gray, for: .disabled)
signOutButton.addTarget(self, action: #selector(signOut(_:)), for: .touchUpInside)
self.view.addSubview(signOutButton)
signOutButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
signOutButton.topAnchor.constraint(equalTo: callGraphButton.bottomAnchor, constant: 10.0).isActive = true
signOutButton.widthAnchor.constraint(equalToConstant: 150.0).isActive = true
signOutButton.heightAnchor.constraint(equalToConstant: 50.0).isActive = true
let deviceModeButton = UIButton()
deviceModeButton.translatesAutoresizingMaskIntoConstraints = false
deviceModeButton.setTitle("Get device info", for: .normal);
deviceModeButton.setTitleColor(.blue, for: .normal);
deviceModeButton.addTarget(self, action: #selector(getDeviceMode(_:)), for: .touchUpInside)
self.view.addSubview(deviceModeButton)
deviceModeButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
deviceModeButton.topAnchor.constraint(equalTo: signOutButton.bottomAnchor, constant: 10.0).isActive = true
deviceModeButton.widthAnchor.constraint(equalToConstant: 150.0).isActive = true
deviceModeButton.heightAnchor.constraint(equalToConstant: 50.0).isActive = true
// Add logging textfield
loggingText = UITextView()
loggingText.isUserInteractionEnabled = false
loggingText.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(loggingText)
loggingText.topAnchor.constraint(equalTo: deviceModeButton.bottomAnchor, constant: 10.0).isActive = true
loggingText.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 10.0).isActive = true
loggingText.rightAnchor.constraint(equalTo: self.view.rightAnchor, constant: -10.0).isActive = true
loggingText.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 10.0).isActive = true
}
func platformViewDidLoadSetup() {
NotificationCenter.default.addObserver(self,
selector: #selector(appCameToForeGround(notification:)),
name: UIApplication.willEnterForegroundNotification,
object: nil)
}
@objc func appCameToForeGround(notification: Notification) {
self.loadCurrentAccount()
}
Interface utilisateur macOS
var callGraphButton: NSButton!
var loggingText: NSTextView!
var signOutButton: NSButton!
var usernameLabel: NSTextField!
func initUI() {
usernameLabel = NSTextField()
usernameLabel.translatesAutoresizingMaskIntoConstraints = false
usernameLabel.stringValue = ""
usernameLabel.isEditable = false
usernameLabel.isBezeled = false
self.view.addSubview(usernameLabel)
usernameLabel.topAnchor.constraint(equalTo: view.topAnchor, constant: 30.0).isActive = true
usernameLabel.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -10.0).isActive = true
// Add call Graph button
callGraphButton = NSButton()
callGraphButton.translatesAutoresizingMaskIntoConstraints = false
callGraphButton.title = "Call Microsoft Graph API"
callGraphButton.target = self
callGraphButton.action = #selector(callGraphAPI(_:))
callGraphButton.bezelStyle = .rounded
self.view.addSubview(callGraphButton)
callGraphButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
callGraphButton.topAnchor.constraint(equalTo: view.topAnchor, constant: 50.0).isActive = true
callGraphButton.heightAnchor.constraint(equalToConstant: 34.0).isActive = true
// Add sign out button
signOutButton = NSButton()
signOutButton.translatesAutoresizingMaskIntoConstraints = false
signOutButton.title = "Sign Out"
signOutButton.target = self
signOutButton.action = #selector(signOut(_:))
signOutButton.bezelStyle = .texturedRounded
self.view.addSubview(signOutButton)
signOutButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
signOutButton.topAnchor.constraint(equalTo: callGraphButton.bottomAnchor, constant: 10.0).isActive = true
signOutButton.heightAnchor.constraint(equalToConstant: 34.0).isActive = true
signOutButton.isEnabled = false
// Add logging textfield
loggingText = NSTextView()
loggingText.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(loggingText)
loggingText.topAnchor.constraint(equalTo: signOutButton.bottomAnchor, constant: 10.0).isActive = true
loggingText.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 10.0).isActive = true
loggingText.rightAnchor.constraint(equalTo: self.view.rightAnchor, constant: -10.0).isActive = true
loggingText.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -10.0).isActive = true
loggingText.widthAnchor.constraint(equalToConstant: 500.0).isActive = true
loggingText.heightAnchor.constraint(equalToConstant: 300.0).isActive = true
}
func platformViewDidLoadSetup() {}
Ensuite, à l’intérieur de la classe ViewController
, remplacez la méthode viewDidLoad()
par :
override func viewDidLoad() {
super.viewDidLoad()
initUI()
do {
try self.initMSAL()
} catch let error {
self.updateLogging(text: "Unable to create Application Context \(error)")
}
self.loadCurrentAccount()
self.platformViewDidLoadSetup()
}
Utiliser MSAL
Initialiser MSAL
Ajoutez la méthode initMSAL
à la classe ViewController
:
func initMSAL() throws {
guard let authorityURL = URL(string: kAuthority) else {
self.updateLogging(text: "Unable to create authority URL")
return
}
let authority = try MSALAADAuthority(url: authorityURL)
let msalConfiguration = MSALPublicClientApplicationConfig(clientId: kClientID, redirectUri: nil, authority: authority)
self.applicationContext = try MSALPublicClientApplication(configuration: msalConfiguration)
self.initWebViewParams()
}
Toujours dans la classe ViewController
et après la méthode initMSAL
, ajoutez la méthode initWebViewParams
:
Code iOS :
func initWebViewParams() {
self.webViewParameters = MSALWebviewParameters(authPresentationViewController: self)
}
Code macOS :
func initWebViewParams() {
self.webViewParameters = MSALWebviewParameters()
}
Gérer le rappel de connexion (iOS uniquement)
Ouvrez le fichier AppDelegate.swift. Pour gérer le rappel après la connexion, ajoutez MSALPublicClientApplication.handleMSALResponse
à la classe appDelegate
comme suit :
// Inside AppDelegate...
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
return MSALPublicClientApplication.handleMSALResponse(url, sourceApplication: options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String)
}
Si vous utilisez Xcode 11, vous devez placer le rappel MSAL dans le fichier SceneDelegate.swift. Si vous prenez en charge à la fois UISceneDelegate et UIApplicationDelegate pour assurer la compatibilité avec une version plus ancienne d’iOS, le rappel MSAL doit être placé dans les deux fichiers.
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
guard let urlContext = URLContexts.first else {
return
}
let url = urlContext.url
let sourceApp = urlContext.options.sourceApplication
MSALPublicClientApplication.handleMSALResponse(url, sourceApplication: sourceApp)
}
Acquérir des jetons
Nous pouvons maintenant implémenter la logique de traitement de l’interface utilisateur de l’application et obtenir des jetons de manière interactive par le biais de MSAL.
MSAL propose deux méthodes principales pour obtenir des jetons : acquireTokenSilently()
et acquireTokenInteractively()
.
acquireTokenSilently()
tente de connecter un utilisateur et d’obtenir des jetons sans intervention de l’utilisateur tant qu’un compte est présent.acquireTokenSilently()
nécessite unMSALAccount
valide, qui peut être récupéré en utilisant l’une des API d’énumération de comptes MSAL. Ce tutoriel utiliseapplicationContext.getCurrentAccount(with: msalParameters, completionBlock: {})
pour récupérer le compte actuel.acquireTokenInteractively()
affiche toujours l’interface utilisateur lors de la tentative de connexion de l’utilisateur. Elle peut utiliser des cookies de session dans le navigateur ou un compte dans Microsoft Authenticator pour fournir une expérience d’authentification unique interactive.
Ajoutez le code suivant à la classe ViewController
:
func getGraphEndpoint() -> String {
return kGraphEndpoint.hasSuffix("/") ? (kGraphEndpoint + "v1.0/me/") : (kGraphEndpoint + "/v1.0/me/");
}
@objc func callGraphAPI(_ sender: AnyObject) {
self.loadCurrentAccount { (account) in
guard let currentAccount = account else {
// We check to see if we have a current logged in account.
// If we don't, then we need to sign someone in.
self.acquireTokenInteractively()
return
}
self.acquireTokenSilently(currentAccount)
}
}
typealias AccountCompletion = (MSALAccount?) -> Void
func loadCurrentAccount(completion: AccountCompletion? = nil) {
guard let applicationContext = self.applicationContext else { return }
let msalParameters = MSALParameters()
msalParameters.completionBlockQueue = DispatchQueue.main
applicationContext.getCurrentAccount(with: msalParameters, completionBlock: { (currentAccount, previousAccount, error) in
if let error = error {
self.updateLogging(text: "Couldn't query current account with error: \(error)")
return
}
if let currentAccount = currentAccount {
self.updateLogging(text: "Found a signed in account \(String(describing: currentAccount.username)). Updating data for that account...")
self.updateCurrentAccount(account: currentAccount)
if let completion = completion {
completion(self.currentAccount)
}
return
}
self.updateLogging(text: "Account signed out. Updating UX")
self.accessToken = ""
self.updateCurrentAccount(account: nil)
if let completion = completion {
completion(nil)
}
})
}
Obtenir un jeton de manière interactive
L’extrait de code suivant obtient un jeton pour la première fois en créant un objet MSALInteractiveTokenParameters
et en appelant acquireToken
. Ensuite, vous ajoutez du code qui :
- Crée
MSALInteractiveTokenParameters
avec des étendues. - Appelle
acquireToken()
avec les paramètres créés. - Gère les erreurs. Pour plus d’informations, consultez le guide de gestion des erreurs MSAL pour iOS et macOS.
- Gère le cas de réussite.
Ajoutez le code suivant à la classe ViewController
.
func acquireTokenInteractively() {
guard let applicationContext = self.applicationContext else { return }
guard let webViewParameters = self.webViewParameters else { return }
// #1
let parameters = MSALInteractiveTokenParameters(scopes: kScopes, webviewParameters: webViewParameters)
parameters.promptType = .selectAccount
// #2
applicationContext.acquireToken(with: parameters) { (result, error) in
// #3
if let error = error {
self.updateLogging(text: "Could not acquire token: \(error)")
return
}
guard let result = result else {
self.updateLogging(text: "Could not acquire token: No result returned")
return
}
// #4
self.accessToken = result.accessToken
self.updateLogging(text: "Access token is \(self.accessToken)")
self.updateCurrentAccount(account: result.account)
self.getContentWithToken()
}
}
La propriété promptType
de MSALInteractiveTokenParameters
configure le comportement des invites d’authentification et de consentement. Les valeurs suivantes sont admises :
.promptIfNecessary
(par défaut) - Des invites sont envoyées à l’utilisateur uniquement en cas de besoin. L’expérience d’authentification unique est déterminée par la présence de cookies dans la vue web et le type de compte. Si plusieurs utilisateurs sont connectés, l’expérience de sélection de compte est présentée. Il s’agit du comportement par défaut..selectAccount
- Si aucun utilisateur n’est spécifié, la vue web d’authentification liste les comptes actuellement connectés parmi lesquels l’utilisateur peut faire son choix..login
- Oblige l’utilisateur à s’authentifier dans la vue web. Si vous spécifiez cette valeur, un seul compte peut être connecté à la fois..consent
- Oblige l’utilisateur à donner son consentement à l’ensemble actuel d’étendues pour la demande.
Obtenir un jeton en mode silencieux
Pour obtenir un jeton mis à jour en mode silencieux, ajoutez le code suivant à la classe ViewController
. Il crée un objet MSALSilentTokenParameters
et appelle acquireTokenSilent()
:
func acquireTokenSilently(_ account : MSALAccount!) {
guard let applicationContext = self.applicationContext else { return }
/**
Acquire a token for an existing account silently
- forScopes: Permissions you want included in the access token received
in the result in the completionBlock. Not all scopes are
guaranteed to be included in the access token returned.
- account: An account object that we retrieved from the application object before that the
authentication flow will be locked down to.
- completionBlock: The completion block that will be called when the authentication
flow completes, or encounters an error.
*/
let parameters = MSALSilentTokenParameters(scopes: kScopes, account: account)
applicationContext.acquireTokenSilent(with: parameters) { (result, error) in
if let error = error {
let nsError = error as NSError
// interactionRequired means we need to ask the user to sign-in. This usually happens
// when the user's Refresh Token is expired or if the user has changed their password
// among other possible reasons.
if (nsError.domain == MSALErrorDomain) {
if (nsError.code == MSALError.interactionRequired.rawValue) {
DispatchQueue.main.async {
self.acquireTokenInteractively()
}
return
}
}
self.updateLogging(text: "Could not acquire token silently: \(error)")
return
}
guard let result = result else {
self.updateLogging(text: "Could not acquire token: No result returned")
return
}
self.accessToken = result.accessToken
self.updateLogging(text: "Refreshed Access token is \(self.accessToken)")
self.updateSignOutButton(enabled: true)
self.getContentWithToken()
}
}
Appeler l’API Microsoft Graph
Après avoir obtenu un jeton, votre application peut l’utiliser dans l’en-tête HTTP pour envoyer une requête autorisée à Microsoft Graph :
clé d’en-tête | value |
---|---|
Autorisation | Porteur <access-token> |
Ajoutez le code suivant à la classe ViewController
:
func getContentWithToken() {
// Specify the Graph API endpoint
let graphURI = getGraphEndpoint()
let url = URL(string: graphURI)
var request = URLRequest(url: url!)
// Set the Authorization header for the request. We use Bearer tokens, so we specify Bearer + the token we got from the result
request.setValue("Bearer \(self.accessToken)", forHTTPHeaderField: "Authorization")
URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error {
self.updateLogging(text: "Couldn't get graph result: \(error)")
return
}
guard let result = try? JSONSerialization.jsonObject(with: data!, options: []) else {
self.updateLogging(text: "Couldn't deserialize result JSON")
return
}
self.updateLogging(text: "Result from Graph: \(result))")
}.resume()
}
Pour en savoir plus sur l’API Microsoft Graph, consultez Microsoft Graph API.
Utiliser MSAL pour se déconnecter
Ajoutez ensuite la prise en charge de la déconnexion.
Important
La déconnexion de MSAL entraîne la suppression de toutes les informations connues sur un utilisateur dans l’application ainsi que la suppression d’une session active sur son appareil quand c’est autorisé par la configuration de l’appareil. Vous pouvez aussi déconnecter l’utilisateur du navigateur.
Pour ajouter une fonctionnalité de déconnexion, ajoutez le code suivant dans la classe ViewController
.
@objc func signOut(_ sender: AnyObject) {
guard let applicationContext = self.applicationContext else { return }
guard let account = self.currentAccount else { return }
do {
/**
Removes all tokens from the cache for this application for the provided account
- account: The account to remove from the cache
*/
let signoutParameters = MSALSignoutParameters(webviewParameters: self.webViewParameters!)
signoutParameters.signoutFromBrowser = false // set this to true if you also want to signout from browser or webview
applicationContext.signout(with: account, signoutParameters: signoutParameters, completionBlock: {(success, error) in
if let error = error {
self.updateLogging(text: "Couldn't sign out account with error: \(error)")
return
}
self.updateLogging(text: "Sign out completed successfully")
self.accessToken = ""
self.updateCurrentAccount(account: nil)
})
}
}
Activer la mise en cache des jetons
Par défaut, MSAL met en cache les jetons de votre application dans le trousseau iOS ou macOS.
Pour activer la mise en cache des jetons
- Vérifiez que votre application est signée correctement.
- Accédez à vos paramètres de projet Xcode >onglet Capacités>Activer le partage de trousseau.
- Sélectionnez + et entrez un des Groupes de trousseaux suivants :
- iOS :
com.microsoft.adalcache
- MacOS :
com.microsoft.identity.universalstorage
- iOS :
Ajouter des méthodes d’assistance
Ajoutez les méthodes d’assistance suivantes à la classe ViewController
pour compléter l’exemple.
Interface utilisateur iOS :
func updateLogging(text : String) {
if Thread.isMainThread {
self.loggingText.text = text
} else {
DispatchQueue.main.async {
self.loggingText.text = text
}
}
}
func updateSignOutButton(enabled : Bool) {
if Thread.isMainThread {
self.signOutButton.isEnabled = enabled
} else {
DispatchQueue.main.async {
self.signOutButton.isEnabled = enabled
}
}
}
func updateAccountLabel() {
guard let currentAccount = self.currentAccount else {
self.usernameLabel.text = "Signed out"
return
}
self.usernameLabel.text = currentAccount.username
}
func updateCurrentAccount(account: MSALAccount?) {
self.currentAccount = account
self.updateAccountLabel()
self.updateSignOutButton(enabled: account != nil)
}
Interface utilisateur macOS :
func updateLogging(text : String) {
if Thread.isMainThread {
self.loggingText.string = text
} else {
DispatchQueue.main.async {
self.loggingText.string = text
}
}
}
func updateSignOutButton(enabled : Bool) {
if Thread.isMainThread {
self.signOutButton.isEnabled = enabled
} else {
DispatchQueue.main.async {
self.signOutButton.isEnabled = enabled
}
}
}
func updateAccountLabel() {
guard let currentAccount = self.currentAccount else {
self.usernameLabel.stringValue = "Signed out"
return
}
self.usernameLabel.stringValue = currentAccount.username ?? ""
self.usernameLabel.sizeToFit()
}
func updateCurrentAccount(account: MSALAccount?) {
self.currentAccount = account
self.updateAccountLabel()
self.updateSignOutButton(enabled: account != nil)
}
iOS uniquement : obtenir des informations supplémentaires sur l’appareil
Utilisez le code suivant pour lire la configuration actuelle de l’appareil, notamment si l’appareil est configuré comme étant partagé :
@objc func getDeviceMode(_ sender: AnyObject) {
if #available(iOS 13.0, *) {
self.applicationContext?.getDeviceInformation(with: nil, completionBlock: { (deviceInformation, error) in
guard let deviceInfo = deviceInformation else {
self.updateLogging(text: "Device info not returned. Error: \(String(describing: error))")
return
}
let isSharedDevice = deviceInfo.deviceMode == .shared
let modeString = isSharedDevice ? "shared" : "private"
self.updateLogging(text: "Received device info. Device is in the \(modeString) mode.")
})
} else {
self.updateLogging(text: "Running on older iOS. GetDeviceInformation API is unavailable.")
}
}
Applications multicompte
Cette application est adaptée à un scénario de compte unique. MSAL prend également en charge les scénarios multicomptes, mais cela nécessite du travail d’application supplémentaire. Vous devez créer l’interface utilisateur pour aider les utilisateurs à sélectionner le compte qu’ils souhaitent utiliser pour chaque action nécessitant des jetons. Sinon, votre application peut implémenter une approche heuristique pour sélectionner le compte à utiliser en interrogeant tous les comptes auprès de MSAL. Par exemple, consultez l’API accountsFromDeviceForParameters:completionBlock:
Test de l'application
Générez et déployez l’application sur un appareil de test ou un simulateur. Vous devez pouvoir vous connecter et obtenir des jetons pour les comptes Microsoft Entra ID ou les comptes personnels Microsoft.
La première fois qu’un utilisateur se connecte à votre application, Microsoft Identity l’invitera à accepter les autorisations demandées. Bien que la plupart des utilisateurs soient à même de donner leur accord, certains locataires Microsoft Entra ont désactivé le consentement de l’utilisateur, ce qui oblige les administrateurs à donner leur consentement au nom de tous les utilisateurs. Pour prendre en charge ce scénario, enregistrez les étendues de votre application.
Une fois que vous êtes connecté, l’application affiche les données retournées par le point de terminaison /me
Microsoft Graph.
Étapes suivantes
Apprenez-en davantage sur la création d’applications mobiles qui appellent des API web protégées dans notre série de scénarios en plusieurs parties.