Esercitazione: Chiamare più API nell'app iOS/macOS usando l'autenticazione nativa

Si applica a: cerchio verde con un simbolo di segno di spunta bianco che indica il contenuto seguente si applica ai tenant esterni. Tenant esterni (altre informazioni)

Questa esercitazione illustra come acquisire un token di accesso e chiamare un'API nell'app iOS/macOS. Microsoft Authentication Library (MSAL) native authentication SDK per iOS/macOS consente di acquisire più token di accesso con un single sign-in. Questa funzionalità consente di acquisire uno o più token di accesso senza richiedere a un utente di ripetere l'autenticazione.

In questa esercitazione, farai:

  • Acquisire uno o più token di accesso.
  • Chiamare un'API

Prerequisiti

Acquisire uno o più token di accesso

MSAL native authentication SDK può archiviare più token di accesso. Dopo l'accesso, è possibile ottenere un token di accesso usando la funzione getAccessToken(parameters:) e specificando gli ambiti per il nuovo token di accesso che si vuole concedere.

  1. Dichiarare e impostare i valori per un set di ambiti API usando il frammento di codice seguente:

    let protectedAPIUrl1: String? = nil
    let protectedAPIUrl2: String? = nil 
    let protectedAPIScopes1: [String] = []
    let protectedAPIScopes2: [String] = []
    
    var accessTokenAPI1: String?
    var accessTokenAPI2: String?
    
    • Inizializzare protectedAPIUrl1 con l'URL della prima API Web.
    • Inizializza protectedAPIUrl2 con l'URL della seconda API Web.
    • Definire protectedAPIScopes1 con ambiti per la prima API, ad esempio ["api://<Resource_App_ID>/ToDoList.Read", "api://<Resource_App_ID>/ToDoList.ReadWrite"].
    • Definire protectedAPIScopes2 con ambiti per la seconda API, in modo simile a protectedAPIScopes1.
    • Dichiara le variabili stringa facoltative accessTokenAPI1 e accessTokenAPI2.
  2. Effettua l'accesso come utente utilizzando il frammento di codice seguente:

    @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...")
        let parameters = MSALNativeAuthSignInParameters(username: email)
        parameters.password = password
        nativeAuth.signIn(parameters: parameters, delegate: self)
    }
    

    Il metodo signInPressed gestisce la pressione del pulsante di accesso. Controlla se i campi di posta elettronica e password sono compilati. Se uno dei due è vuoto, viene visualizzato il messaggio di posta elettronica o la password non impostata. Se entrambi i campi vengono compilati, registra il messaggio di posta elettronica, visualizza "Accesso..." e avvia l'accesso usando il metodo signIn da nativeAuth con il messaggio di posta elettronica e la password specificati. L'SDK recupera un token valido per gli ambiti OIDC predefiniti (openid, offline_access, profile) perché non vengono specificati ambiti.

  3. Acquisire uno o più token di accesso usando il frammento di codice seguente:

    @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 {
            let parameters = MSALNativeAuthGetAccessTokenParameters()
            parameters.scopes = protectedAPIScopes1
            accountResult?.getAccessToken(parameters: parameters, 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 {
            let parameters = MSALNativeAuthGetAccessTokenParameters()
            parameters.scopes = protectedAPIScopes2
            accountResult?.getAccessToken(parameters: parameters, delegate: self)
            let message = "Retrieving access token to use with API 2..."
            showResultText(message)
            print(message)
        }
    }
    

    I metodi protectedApi1Pressed e protectedApi2Pressed gestiscono il processo di acquisizione dei token di accesso per due set distinti di ambiti. Prima di tutto assicurano che l'URL e gli ambiti di ogni API siano configurati correttamente. Se è già disponibile un token di accesso per l'API, accede direttamente all'API. In caso contrario, richiede un token di accesso e informa l'utente del processo di recupero dei token in corso.

    Per assegnare un token di accesso a protectedAPIScopes1 e protectedAPIScopes2, usare il frammento di codice seguente:

    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")")
    }
    

    Il metodo onAccessTokenRetrieveCompleted stampa il token di accesso nella console. Controlla quindi se protectedAPIScopes1 sono inclusi negli ambiti del risultato e se protectedAPIUrl1 è disponibile; in tal caso, imposta accessTokenAPI1 e chiama accessProtectedAPI con l'URL e il token. Esegue un controllo simile per protectedAPIScopes2 e protectedAPIUrl2, aggiornando accessTokenAPI2 e effettuando la chiamata API se vengono soddisfatte le condizioni. Infine, il metodo visualizza un messaggio con lo stato, gli ambiti e il token di accesso connessi e aggiorna l'interfaccia utente.

    Il metodo onAccessTokenRetrieveError visualizza un messaggio di errore con la descrizione dell'errore di recupero del token di accesso o un messaggio predefinito se non viene fornita alcuna descrizione.

Chiamare un'API

Usare i frammenti di codice seguenti per chiamare un'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()
}

Il metodo accessProtectedAPI invia una richiesta GET all'endpoint API specificato usando il token di accesso fornito. Configura la richiesta con il token nell'intestazione Autorizzazione. Quando riceve una risposta corretta (codice di stato HTTP 200-299), deserializza i dati JSON e aggiorna l'interfaccia utente con il codice di stato HTTP e il corpo della risposta. Se si verifica un errore durante la gestione della richiesta o della risposta, viene visualizzato il messaggio di errore nell'interfaccia utente. Questo metodo consente l'accesso all'API 1 o all'API 2, a seconda dell'URL e del token di accesso forniti.