Partager via


Tutoriel : appeler plusieurs API dans une application iOS/macOS en utilisant l’authentification native

S’applique à : Cercle vert avec un symbole de coche blanche. iOS (Swift) Cercle vert avec un symbole de coche blanche. macOS (Swift)

Dans ce tutoriel, vous allez apprendre à acquérir un jeton d’accès et à appeler une API dans votre application iOS/macOS. Le Kit de développement logiciel (SDK) d’authentification native de la bibliothèque d’authentification Microsoft (MSAL) pour iOS/macOS vous permet d’acquérir plusieurs jetons d’accès avec une authentification unique. Cette fonctionnalité vous permet d’acquérir un ou plusieurs jetons d’accès sans demander à un utilisateur de se réauthentifier.

Dans ce tutoriel, vous allez apprendre à :

  • Acquérir un ou plusieurs jetons d’accès.
  • Appeler une API

Prérequis

Acquérir un ou plusieurs jetons d’accès

Le Kit de développement logiciel (SDK) d’authentification native de MSAL peut stocker plusieurs jetons d’accès. Après vous être connecté, vous pouvez obtenir un jeton d’accès à l’aide de la fonction getAccessToken(scope:) et spécifier les étendues du nouveau jeton d’accès que vous souhaitez accorder.

  1. Déclarez et définissez des valeurs pour un ensemble d’étendues d’API à l’aide de l’extrait de code suivant :

    let protectedAPIUrl1: String? = nil
    let protectedAPIUrl2: String? = nil 
    let protectedAPIScopes1: [String] = []
    let protectedAPIScopes2: [String] = []
    
    var accessTokenAPI1: String?
    var accessTokenAPI2: String?
    
    • Initialisez protectedAPIUrl1 avec l’URL de votre première API web.
    • Initialisez protectedAPIUrl2 avec l’URL de votre deuxième API web.
    • Définissez protectedAPIScopes1 avec des étendues pour votre première API, par exemple ["api://<Resource_App_ID>/ToDoList.Read", "api://<Resource_App_ID>/ToDoList.ReadWrite"].
    • Définissez protectedAPIScopes2 avec des étendues pour votre deuxième API, similaire à protectedAPIScopes1.
    • Déclarez les variables de chaîne facultatives accessTokenAPI1 et accessTokenAPI2.
  2. Connecte l’utilisateur à l’aide de l’extrait de code suivant :

    @IBAction func signInPressed(_: Any) {
        guard let email = emailTextField.text, let password = passwordTextField.text else {
            resultTextView.text = "Email or password not set"
            return
        }
    
        print("Signing in with email \(email) and password")
    
        showResultText("Signing in...")
    
        nativeAuth.signIn(username: email, password: password, delegate: self)
    }
    

    La méthode signInPressed gère l’appui sur le bouton de connexion. Elle vérifie si les champs d’e-mail et de mot de passe sont remplis. Si l’un ou l’autre est vide, elle affiche E-mail ou mot de passe non défini. Si les deux champs sont remplis, elle enregistre l’e-mail, affiche Connexion en cours... et lance la connexion à l’aide de la méthode signIn à partir de nativeAuth avec l’e-mail et le mot de passe fournis. Le Kit de développement logiciel (SDK) récupère un jeton valide pour les étendues OIDC par défaut (openid, offline_access, profile), car aucune étendue n’est spécifiée.

  3. Acquérir un ou plusieurs jetons d’accès à l’aide de l’extrait de code suivant :

    @IBAction func protectedApi1Pressed(_: Any) {
        guard let url = protectedAPIUrl1, !protectedAPIScopes1.isEmpty else {
            showResultText("API 1 not configured.")
            return
        }
    
        if let accessToken = accessTokenAPI1 {
            accessProtectedAPI(apiUrl: url, accessToken: accessToken)
        } else {
            accountResult?.getAccessToken(scopes: protectedAPIScopes1, delegate: self)
            let message = "Retrieving access token to use with API 1..."
            showResultText(message)
            print(message)
        }
    }
    
    @IBAction func protectedApi2Pressed(_: Any) {
        guard let url = protectedAPIUrl2, !protectedAPIScopes2.isEmpty else {
            showResultText("API 2 not configured.")
            return
        }
    
        if let accessToken = accessTokenAPI2 {
            accessProtectedAPI(apiUrl: url, accessToken: accessToken)
        } else {
            accountResult?.getAccessToken(scopes: protectedAPIScopes2, delegate: self)
            let message = "Retrieving access token to use with API 2..."
            showResultText(message)
            print(message)
        }
    }
    

    Les méthodes protectedApi1Pressed et protectedApi2Pressed gèrent le processus d’acquisition de jetons d’accès pour deux ensembles distincts d’étendues. Elles s’assurent d’abord que l’URL et les étendues de chaque API sont correctement configurées. Si un jeton d’accès pour l’API est déjà disponible, elle accède directement à l’API. Sinon, elle demande un jeton d’accès et informe l’utilisateur du processus de récupération de jeton en cours.

    Pour affecter un jeton d’accès à protectedAPIScopes1 et protectedAPIScopes2, utilisez l’extrait suivant :

    func onAccessTokenRetrieveCompleted(result: MSALNativeAuthTokenResult) {
        print("Access Token: \(result.accessToken)")
    
        if protectedAPIScopes1.allSatisfy(result.scopes.contains),
           let url = protectedAPIUrl1
        {
            accessTokenAPI1 = result.accessToken
            accessProtectedAPI(apiUrl: url, accessToken: result.accessToken)
        }
    
        if protectedAPIScopes2.allSatisfy(result.scopes.contains(_:)),
           let url = protectedAPIUrl2
        {
            accessTokenAPI2 = result.accessToken
            accessProtectedAPI(apiUrl: url, accessToken: result.accessToken)
        }
    
        showResultText("Signed in." + "\n\n" + "Scopes:\n\(result.scopes)" + "\n\n" + "Access Token:\n\(result.accessToken)")
        updateUI()
    }
    
    func onAccessTokenRetrieveError(error: MSAL.RetrieveAccessTokenError) {
        showResultText("Error retrieving access token: \(error.errorDescription ?? "No error description")")
    }
    

    La méthode onAccessTokenRetrieveCompleted affiche le jeton d’accès dans la console. Elle vérifie ensuite si des protectedAPIScopes1 sont incluses dans les étendues du résultat et si une protectedAPIUrl1 est disponible. Si c’est le cas, elle définit l’accessTokenAPI1 et appelle l’accessProtectedAPI avec l’URL et le jeton. Elle effectue une vérification similaire pour protectedAPIScopes2 et protectedAPIUrl2 en mettant à jour l’accessTokenAPI2 et en appelant l’API si les conditions sont remplies. Pour finir, la méthode affiche un message avec l’état de connexion, les étendues et le jeton d’accès, et met à jour l’interface utilisateur.

    La méthode onAccessTokenRetrieveError affiche un message d’erreur avec la description de l’erreur de récupération du jeton d’accès ou un message par défaut si aucune description n’est fournie.

Appeler une API

Utilisez les extraits de code suivants pour appeler une API :

func accessProtectedAPI(apiUrl: String, accessToken: String) {
    guard let url = URL(string: apiUrl) else {
        let errorMessage = "Invalid API url"
        print(errorMessage)
        DispatchQueue.main.async {
            self.showResultText(errorMessage)
        }
        return
    }
    
    var request = URLRequest(url: url)
    request.httpMethod = "GET"
    request.setValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization")
    
    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        if let error = error {
            print("Error found when accessing API: \(error.localizedDescription)")
            DispatchQueue.main.async {
                self.showResultText(error.localizedDescription)
            }
            return
        }
        
        guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode)
        else {
            DispatchQueue.main.async {
                self.showResultText("Unsuccessful response found when accessing the API")
            }
            return
        }
        
        guard let data = data, let result = try? JSONSerialization.jsonObject(with: data, options: []) else {
            DispatchQueue.main.async {
                self.showResultText("Couldn't deserialize result JSON")
            }
            return
        }
        
        DispatchQueue.main.async {
            self.showResultText("""
                            Accessed API successfully using access token.
                            HTTP response code: \(httpResponse.statusCode)
                            HTTP response body: \(result)
                            """)
        }
    }
    
    task.resume()
}

La méthode accessProtectedAPI envoie une requête GET au point de terminaison d’API spécifié en utilisant le jeton d’accès fourni. Elle configure la requête avec le jeton dans l’en-tête d’autorisation. Lorsqu’elle reçoit une réponse de réussite (code d’état HTTP 200-299), elle désérialise les données JSON et met à jour l’interface utilisateur avec le code d’état HTTP et le corps de la réponse. Si une erreur se produit pendant la gestion de la requête ou de la réponse, elle affiche le message d’erreur dans l’interface utilisateur. Cette méthode permet d’accéder à l’API 1 ou à l’API 2, en fonction de l’URL et du jeton d’accès fournis.