Tutorial: chamar várias APIs no aplicativo iOS/macOS usando autenticação nativa

Aplica-se a: Círculo verde com um símbolo de marca de seleção branca que indica que o conteúdo a seguir se aplica a locatários externos. Locatários externos (saiba mais)

Nesse tutorial, você aprenderá como adquirir um token de acesso e chamar uma API em seu aplicativo iOS/macOS. O SDK de autenticação nativa da MSAL (biblioteca de autenticação da Microsoft) para iOS/macOS permite adquirir vários tokens de acesso com um único login. Esse recurso permite adquirir um ou mais tokens de acesso sem exigir que o usuário se reautentique.

Neste tutorial, você:

  • Adquira um ou vários tokens de acesso.
  • Chamar uma API

Pré-requisitos

Adquirir um ou vários tokens de acesso

O SDK de autenticação nativa da MSAL pode armazenar vários tokens de acesso. Depois de entrar, você pode obter um token de acesso usando a função getAccessToken(parameters:) e especificando os escopos para o novo token de acesso que deseja conceder.

  1. Declare e defina valores para um conjunto de escopos de API usando o seguinte snippet de código:

    let protectedAPIUrl1: String? = nil
    let protectedAPIUrl2: String? = nil 
    let protectedAPIScopes1: [String] = []
    let protectedAPIScopes2: [String] = []
    
    var accessTokenAPI1: String?
    var accessTokenAPI2: String?
    
    • Inicialize protectedAPIUrl1 com a URL da sua primeira API Web.
    • Inicialize protectedAPIUrl2 com a URL da segunda API Web.
    • Defina protectedAPIScopes1 com escopos para sua primeira API, como ["api://<Resource_App_ID>/ToDoList.Read", "api://<Resource_App_ID>/ToDoList.ReadWrite"].
    • Defina protectedAPIScopes2 com escopos para sua segunda API, semelhante ao protectedAPIScopes1.
    • Declare as variáveis de cadeia de caracteres opcionais accessTokenAPI1 e accessTokenAPI2.
  2. Faça login do usuário usando o seguinte snippet de código:

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

    O método signInPressed manipula o pressionamento do botão de entrada. Ele verifica se os campos de e-mail e senha estão preenchidos. Se um deles estiver vazio, ele mostrará "Email ou senha não definidos". Se ambos os campos estiverem preenchidos, ele registrará o email, exibirá "Fazendo login..." e iniciará a entrada usando o método signIn de nativeAuth com o e-mail e a senha fornecidos. O SDK recupera um token válido para os escopos OIDC padrão (openid, offline_access, profile) porque nenhum escopo é especificado.

  3. Adquira um ou vários tokens de acesso usando o seguinte snippet de código:

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

    Os métodos protectedApi1Pressed e protectedApi2Pressed gerenciam o processo de aquisição de tokens de acesso para dois conjuntos distintos de escopos. Primeiro, eles garantem que cada URL e os escopos de cada API estejam configurados corretamente. Se um token de acesso para a API já estiver disponível, ele acessará diretamente a API. Caso contrário, ele solicitará um token de acesso e informará o usuário sobre o processo de recuperação de token em andamento.

    Para atribuir um token de acesso a protectedAPIScopes1 e protectedAPIScopes2, use o seguinte snippet:

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

    O método onAccessTokenRetrieveCompleted imprime o token de acesso no console. Em seguida, ele verifica se protectedAPIScopes1 está incluído nos escopos do resultado e se protectedAPIUrl1 está disponível; em caso afirmativo, ele define accessTokenAPI1 e chama accessProtectedAPI com a URL e o token. Ele executa uma verificação semelhante para protectedAPIScopes2 e protectedAPIUrl2, atualizando accessTokenAPI2 e fazendo a chamada à API se as condições forem atendidas. Por fim, o método exibe uma mensagem com o status de autenticação, os escopos e o token de acesso, e atualiza a interface do usuário.

    O método onAccessTokenRetrieveError exibe uma mensagem de erro com a descrição do erro de recuperação do token de acesso ou uma mensagem padrão se nenhuma descrição for fornecida.

Chamar uma API

Use os seguintes trechos de código para chamar uma 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()
}

O método accessProtectedAPI envia uma solicitação GET para o ponto de extremidade de API especificado usando o token de acesso fornecido. Ele configura a solicitação com o token no cabeçalho de autorização. Quando recebe uma resposta bem-sucedida (código de status HTTP 200-299), ele desserializa os dados JSON e atualiza a interface do usuário com o código de status HTTP e o corpo da resposta. Se ocorrer um erro durante o tratamento da solicitação ou resposta, ele exibirá a mensagem de erro na interface do usuário. Esse método permite acesso à API 1 ou API 2, dependendo da URL e do token de acesso fornecidos.