Condividi tramite


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

Si applica aCerchio verde con un segno di spunta bianco. iOS (Swift) Cerchio verde con un segno di spunta bianco. macOS (Swift)

In questa esercitazione, verrà illustrato come acquisire un token di accesso e chiamare un'API nell'app iOS/macOS. L'SDK per l'autenticazione nativa di MSAL (Microsoft Authentication Library) per iOS/macOS consente di acquisire più token di accesso da un singolo accesso. Questa funzionalità consente di acquisire uno o più token di accesso senza richiedere all'utente di eseguire nuovamente l'accesso.

In questa esercitazione apprenderai a:

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

Prerequisiti

Acquisire uno o più token di accesso

L'SDK per l'autenticazione nativa di MSAL può memorizzare più token di accesso. Dopo aver effettuato l'accesso, è possibile ottenere un token di accesso usando la funzione getAccessToken(scope:) e specificando gli ambiti del nuovo token di accesso che si desidera concedere.

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

    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.
    • Inizializzare protectedAPIUrl2 con l'URL della seconda API Web.
    • Definire protectedAPIScopes1 con gli ambiti per la prima API, ad esempio ["api://<Resource_App_ID>/ToDoList.Read", "api://<Resource_App_ID>/ToDoList.ReadWrite"].
    • Definire protectedAPIScopes2 con gli ambiti per la seconda API, simile a protectedAPIScopes1.
    • Dichiarare le variabili stringa opzionali accessTokenAPI1 e accessTokenAPI2.
  2. Consentire l'accesso dell'utente usando il seguente frammento di codice:

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

    Il metodo signInPressed gestisce la pressione del pulsante di accesso. Controlla se i campi e-mail e password sono compilati. Se uno dei due campi è vuoto, verrà visualizzato il messaggio "E-mail o password non impostate". Se invece sono stati compilati entrambi i campi, l'e-mail viene registrata, viene visualizzato il messaggio "Accesso..." e viene avviata la procedura di accesso usando il signIn metodo nativeAuth con l'e-mail e la password fornite. L'SDK recupera un token valido per gli ambiti OIDC predefiniti (openid, offline_access, profile) perché non sono stati specificati ambiti.

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

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

    I metodi protectedApi1Pressed e protectedApi2Pressed gestiscono il processo di acquisizione dei token di accesso per due gruppi distinti di ambiti. Prima di tutto verificano che l'URL e gli ambiti di ciascuna API siano configurati correttamente. Se è già disponibile un token di accesso per l'API, esegue l'accesso diretto all'API. In caso contrario, viene richiesto un token di accesso e l'utente viene informato del processo di recupero del token in corso.

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

    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. Verifica quindi se protectedAPIScopes1 è incluso negli ambiti del risultato e se protectedAPIUrl1 è disponibile; in caso positivo, imposta accessTokenAPI1 e chiama accessProtectedAPI con l'URL e il token. Esegue un controllo simile per protectedAPIScopes2 e protectedAPIUrl2, aggiornando accessTokenAPI2 ed effettuando la chiamata API se le condizioni vengono soddisfatte. Infine, il metodo visualizza un messaggio con lo stato di accesso, gli ambiti e il token di accesso 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 seguenti frammenti di codice 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 dell'autorizzazione. Quando riceve una risposta risciuta (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, visualizza il messaggio di errore nell'interfaccia utente. Questo metodo consente di accedere all'API 1 o all'API 2, a seconda dell'URL e del token di accesso forniti.