Tutorial: Inicie sessão em utilizadores e chame o Microsoft Graph a partir de uma aplicação iOS ou macOS

Neste tutorial, você cria um aplicativo iOS ou macOS que se integra à plataforma de identidade da Microsoft para assinar usuários e obter um token de acesso para chamar a API do Microsoft Graph.

Depois de concluir o tutorial, seu aplicativo aceita entradas de contas pessoais da Microsoft (incluindo outlook.com, live.com e outras) e contas corporativas ou de estudante de qualquer empresa ou organização que use o Microsoft Entra ID. Este tutorial é aplicável a aplicativos iOS e macOS. Algumas etapas são diferentes entre as duas plataformas.

Neste tutorial:

  • Criar um projeto de aplicativo iOS ou macOS no Xcode
  • Registar a aplicação no centro de administração do Microsoft Entra
  • Adicionar código para dar suporte ao login e logout do usuário
  • Adicionar código para chamar a API do Microsoft Graph
  • Testar a aplicação

Pré-requisitos

Como funciona o aplicativo tutorial

Screenshot of how the sample app generated by this tutorial works.

O aplicativo neste tutorial pode entrar usuários e obter dados do Microsoft Graph em seu nome. Esses dados são acessados por meio de uma API protegida (Microsoft Graph API, neste caso) que requer autorização e é protegida pela plataforma de identidade da Microsoft.

Mais especificamente:

  • Seu aplicativo entra no usuário por meio de um navegador ou do Microsoft Authenticator.
  • O usuário final aceita as permissões solicitadas pelo aplicativo.
  • Seu aplicativo recebe um token de acesso para a API do Microsoft Graph.
  • O token de acesso é incluído na solicitação HTTP para a API da Web.
  • Processe a resposta do Microsoft Graph.

Este exemplo usa a Microsoft Authentication Library (MSAL) para implementar a autenticação. A MSAL renovará automaticamente os tokens, fornecerá logon único (SSO) entre outros aplicativos no dispositivo e gerenciará a(s) conta(s).

Se você quiser baixar uma versão completa do aplicativo que você cria neste tutorial, você pode encontrar ambas as versões no GitHub:

Criar um novo projeto

  1. Abra o Xcode e selecione Criar um novo projeto Xcode.
  2. Para aplicativos iOS, selecione Aplicativo de visualização única do iOS e selecione Avançar>.
  3. Para aplicativos macOS, selecione macOS>Cocoa App e selecione Next.
  4. Forneça um nome de produto.
  5. Defina o idioma como Swift e selecione Avançar.
  6. Selecione uma pasta para criar seu aplicativo e selecione Criar.

Registar a candidatura

Gorjeta

As etapas neste artigo podem variar ligeiramente com base no portal a partir do qual você começou.

  1. Entre no centro de administração do Microsoft Entra como pelo menos um desenvolvedor de aplicativos.
  2. Se você tiver acesso a vários locatários, use o ícone Configurações no menu superior para alternar para o locatário no qual deseja registrar o aplicativo no menu Diretórios + assinaturas.
  3. Navegue até Registros do aplicativo Identity>Applications>.
  4. Selecione Novo registo.
  5. Introduza um Nome para a sua aplicação. Os usuários do seu aplicativo podem ver esse nome e você pode alterá-lo mais tarde.
  6. Selecione Contas em qualquer diretório organizacional (Qualquer diretório Microsoft Entra - Multilocatário) e contas pessoais da Microsoft (por exemplo, Skype, Xbox) em Tipos de conta suportados.
  7. Selecione Registar.
  8. Em Gerir, selecione Autenticação>Adicionar uma plataforma>iOS/macOS.
  9. Insira o ID do pacote do seu projeto. Se tiver baixado o exemplo de código, o ID do pacote é com.microsoft.identitysample.MSALiOS. Se você estiver criando seu próprio projeto, selecione seu projeto no Xcode e abra a guia Geral . O identificador do pacote aparece na seção Identidade .
  10. Selecione Configurar e salvar a Configuração do MSAL que aparece na página de configuração do MSAL para que você possa inseri-la quando configurar seu aplicativo mais tarde.
  11. Selecione Concluído.

Adicionar MSAL

Escolha uma das seguintes maneiras de instalar a biblioteca MSAL em seu aplicativo:

CacauPods

  1. Se você estiver usando o CocoaPods, instale MSAL primeiro criando um arquivo vazio chamado podfile na mesma pasta do arquivo .xcodeproj do seu projeto. Adicione o seguinte ao podfile:

    use_frameworks!
    
    target '<your-target-here>' do
       pod 'MSAL'
    end
    
  2. Substitua <your-target-here> pelo nome do seu projeto.

  3. Em uma janela do terminal, navegue até a pasta que contém o podfile que você criou e execute pod install para instalar a biblioteca MSAL.

  4. Feche o Xcode e abra <your project name>.xcworkspace para recarregar o projeto no Xcode.

Cartago

Se você estiver usando Carthage, instale MSAL adicionando-o ao seu Cartfile:

github "AzureAD/microsoft-authentication-library-for-objc" "master"

Em uma janela de terminal, no mesmo diretório que o Cartfile atualizado, execute o seguinte comando para que Carthage atualize as dependências em seu projeto.

iOS:

carthage update --platform iOS

macOS:

carthage update --platform macOS

Manualmente

Você também pode usar o Submódulo Git ou verificar a versão mais recente para usar como uma estrutura em seu aplicativo.

Adicionar o registo da aplicação

Em seguida, adicionamos o registro do aplicativo ao seu código.

Primeiro, adicione a seguinte instrução import à parte superior do arquivo ViewController.swift e AppDelegate.swift ou SceneDelegate.swift:

import MSAL

Em seguida, adicione o seguinte código a ViewController.swift antes de 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?

O único valor que você modifica é o valor atribuído para kClientID ser sua ID do aplicativo. Esse valor faz parte dos dados de Configuração do MSAL que você salvou durante a etapa no início deste tutorial para registrar o aplicativo.

Definir configurações do projeto Xcode

Adicione um novo grupo de chaveiros ao seu projeto Assinatura & Capacidades. O grupo de chaves deve estar com.microsoft.adalcache no iOS e com.microsoft.identity.universalstorage no macOS.

Xcode UI displaying how the keychain group should be set up.

Apenas para iOS, configure esquemas de URL

Nesta etapa, você se registrará CFBundleURLSchemes para que o usuário possa ser redirecionado de volta ao aplicativo após entrar. A propósito, LSApplicationQueriesSchemes também permite que seu aplicativo use o Microsoft Authenticator.

No Xcode, abra Info.plist como um arquivo de código-fonte e adicione o seguinte dentro da <dict> seção . Substitua [BUNDLE_ID] pelo valor que você usou anteriormente. Se você baixou o código, o identificador do pacote é com.microsoft.identitysample.MSALiOS. Se você estiver criando seu próprio projeto, selecione seu projeto no Xcode e abra a guia Geral . O identificador do pacote aparece na seção Identidade .

<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>

Apenas para macOS, configure o App Sandbox

  1. Vá para a guia>Capacidades das Configurações do Projeto Xcode Sandbox do aplicativo >
  2. Marque a caixa de seleção Conexões de saída (cliente ).

Crie a interface do usuário do seu aplicativo

Agora, crie uma interface do usuário que inclua um botão para chamar a API do Microsoft Graph, outro para sair e uma exibição de texto para ver alguma saída, adicionando o seguinte código à ViewController classe:

Interface do usuário do 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 do usuário do 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() {}

Em seguida, também dentro da ViewController classe, substitua o viewDidLoad() método por:

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

Usar MSAL

Inicializar MSAL

Para a ViewController classe, adicione o initMSAL método:

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

Ainda na classe e após o ViewControllerinitMSAL método, adicione o initWebViewParams método:

Código iOS:

func initWebViewParams() {
        self.webViewParameters = MSALWebviewParameters(authPresentationViewController: self)
    }

Código macOS:

func initWebViewParams() {
        self.webViewParameters = MSALWebviewParameters()
    }

Manipular o retorno de chamada de entrada (somente iOS)

Abra o arquivo AppDelegate.swift . Para lidar com o retorno de chamada após o login, adicione MSALPublicClientApplication.handleMSALResponse à appDelegate classe da seguinte forma:

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

Se você estiver usando o Xcode 11, você deve colocar o retorno de chamada MSAL no SceneDelegate.swift em vez disso. Se você oferecer suporte a UISceneDelegate e UIApplicationDelegate para compatibilidade com iOS mais antigo, o retorno de chamada MSAL precisará ser colocado em ambos os arquivos.

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

Adquira Tokens

Agora, podemos implementar a lógica de processamento da interface do usuário do aplicativo e obter tokens interativamente por meio do MSAL.

MSAL expõe dois métodos principais para obter tokens: acquireTokenSilently() e acquireTokenInteractively().

  • acquireTokenSilently() Tenta iniciar sessão num utilizador e obter tokens sem interação do utilizador, desde que exista uma conta. acquireTokenSilently() exigem um MSALAccountarquivo , que pode ser recuperado usando uma das APIs de enumeração de conta da MSAL. Este tutorial usa applicationContext.getCurrentAccount(with: msalParameters, completionBlock: {}) para recuperar a conta atual.

  • acquireTokenInteractively() sempre mostra a interface do usuário ao tentar entrar no usuário. Ele pode usar cookies de sessão no navegador ou uma conta no autenticador da Microsoft para fornecer uma experiência de SSO interativo.

Adicione o seguinte código à ViewController classe:

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

Obtenha um token interativamente

O trecho de código a seguir obtém um token pela primeira vez criando um MSALInteractiveTokenParameters objeto e chamando acquireToken. Em seguida, você adiciona um código que:

  1. Cria MSALInteractiveTokenParameters com escopos.
  2. Chamadas acquireToken() com os parâmetros criados.
  3. Lida com erros. Para obter mais detalhes, consulte o guia de tratamento de erros do MSAL para iOS e macOS.
  4. Lida com o caso de sucesso.

Adicione o seguinte código à 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()
    }
}

A promptType propriedade de configura o comportamento do prompt de MSALInteractiveTokenParameters autenticação e consentimento. Os seguintes valores são suportados:

  • .promptIfNecessary (padrão) - O usuário é solicitado somente se necessário. A experiência de SSO é determinada pela presença de cookies na visualização da web e pelo tipo de conta. Se vários usuários estiverem conectados, a experiência de seleção de conta será apresentada. Este é o comportamento padrão.
  • .selectAccount - Se nenhum usuário for especificado, o webview de autenticação apresentará uma lista de contas atualmente conectadas para o usuário selecionar.
  • .login - Requer que o usuário se autentique no webview. Apenas uma conta pode ser conectada de cada vez se você especificar esse valor.
  • .consent - Requer que o usuário consinta com o conjunto atual de escopos para a solicitação.

Obtenha um token silenciosamente

Para adquirir um token atualizado silenciosamente, adicione o seguinte código à ViewController classe. Ele cria um MSALSilentTokenParameters objeto e chama 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()
        }
    }

Chamar a API do Microsoft Graph

Depois de ter um token, seu aplicativo pode usá-lo no cabeçalho HTTP para fazer uma solicitação autorizada ao Microsoft Graph:

chave de cabeçalho valor
Autorização Token de acesso ao portador <>

Adicione o seguinte código à ViewController classe:

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

Consulte API do Microsoft Graph para saber mais sobre a API do Microsoft Graph .

Usar o MSAL para sair

Em seguida, adicione suporte para sair.

Importante

Sair com o MSAL remove todas as informações conhecidas sobre um usuário do aplicativo, bem como remover uma sessão ativa em seu dispositivo quando permitido pela configuração do dispositivo. Opcionalmente, você também pode sair do usuário do navegador.

Para adicionar a capacidade de saída, adicione o seguinte código dentro da ViewController classe.

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

        }
    }

Habilitar cache de token

Por padrão, a MSAL armazena em cache os tokens do seu aplicativo nas chaves iOS ou macOS.

Para habilitar o cache de tokens:

  1. Certifique-se de que o seu pedido está devidamente assinado
  2. Vá para a guia>Capacidades das Configurações >do Projeto Xcode Ativar Compartilhamento de Chaves
  3. Selecione + e insira um dos seguintes Grupos de Chaves:
    • iOS: com.microsoft.adalcache
    • macOS: com.microsoft.identity.universalstorage

Adicionar métodos auxiliares

Adicione os seguintes métodos auxiliares à ViewController classe para concluir o exemplo.

Interface do usuário do 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 do usuário do 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)
    }

Apenas iOS: obtenha informações adicionais sobre o dispositivo

Use o código a seguir para ler a configuração atual do dispositivo, incluindo se o dispositivo está configurado como compartilhado:

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

Aplicações multicontas

Este aplicativo foi criado para um cenário de conta única. O MSAL também suporta cenários de várias contas, mas requer mais trabalho de aplicativo. Você precisa criar uma interface do usuário para ajudar os usuários a selecionar qual conta eles desejam usar para cada ação que requer tokens. Como alternativa, seu aplicativo pode implementar uma heurística para selecionar qual conta usar consultando todas as contas do MSAL. Por exemplo, consulte accountsFromDeviceForParameters:completionBlock:API

Testar a sua aplicação

Crie e implante o aplicativo em um dispositivo ou simulador de teste. Você deve ser capaz de entrar e obter tokens para o Microsoft Entra ID ou contas pessoais da Microsoft.

Na primeira vez que um usuário entrar em seu aplicativo, ele será solicitado pela identidade da Microsoft a consentir com as permissões solicitadas. Embora a maioria dos usuários seja capaz de consentir, alguns locatários do Microsoft Entra desabilitaram o consentimento do usuário, que exige que os administradores consintam em nome de todos os usuários. Para dar suporte a esse cenário, registre os escopos do seu aplicativo.

Depois de entrar, o aplicativo exibirá os dados retornados do ponto de extremidade do Microsoft Graph /me .

Próximos passos

Saiba mais sobre como criar aplicativos móveis que chamam APIs da Web protegidas em nossa série de cenários com várias partes.