Tutorial: Conectar usuários e chamar o Microsoft Graph de um aplicativo iOS ou macOS
Neste tutorial, você criará um aplicativo iOS ou macOS que se integra à plataforma de identidade da Microsoft para conectar usuários e obter um token de acesso para chamar a API do Microsoft Graph.
Após você ter concluído esse tutorial, seu aplicativo aceitará credenciais de contas Microsoft pessoais (incluindo outlook.com, live.com e outras) e de contas corporativas ou de estudante de qualquer empresa ou organização que utilize 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
- Registrar o aplicativo no centro de administração do Microsoft Entra
- Adicionar código para entrada e saída do usuário
- Adicionar código para chamar a API do Microsoft Graph
- Testar o aplicativo
Pré-requisitos
Como o aplicativo do tutorial funciona
O aplicativo neste tutorial pode conectar usuários e obter dados do Microsoft Graph no nome deles. Esses dados serão acessados por meio de uma API protegida (API do Microsoft Graph, nesse caso) que exige autorização e é protegida pela plataforma de identidade da Microsoft.
Mais especificamente:
- Seu aplicativo conectará o usuário por meio de um navegador ou do Microsoft Authenticator.
- O usuário final aceitará as permissões solicitadas por seu aplicativo.
- Será emitido um token de acesso para seu aplicativo para a API do Microsoft Graph.
- O token de acesso será incluído na solicitação HTTP para API Web.
- Processe a resposta do Microsoft Graph.
Esta amostra usa a MSAL (Biblioteca de Autenticação da Microsoft) para implementar a Autenticação. A MSAL automaticamente renovará tokens, fornecerá o SSO (logon único) entre outros aplicativos no dispositivo e ajudará a gerenciar as contas.
Se quiser baixar uma versão completa do aplicativo criado neste tutorial, você encontrará ambas as versões no GitHub:
- Exemplo de código para iOS (GitHub)
- Exemplo de código para macOS (GitHub)
Criar um novo projeto
- Abra o Xcode e selecione Crie um novo projeto Xcode.
- Para aplicativos iOS, selecione iOS>Aplicativo de Exibição Única e selecione Avançar.
- Para aplicativos macOS, selecione macOS>Cocoa App e selecione Avançar.
- Forneça um nome de produto.
- Defina a Linguagem como Swift e selecione Avançar.
- Selecione uma pasta na qual criar o aplicativo e selecione Criar.
Registrar o aplicativo
Dica
As etapas neste artigo podem variar ligeiramente com base no portal do qual você começa.
- Faça login no Centro de administração do Microsoft Entra como pelo menos um Desenvolvedor de aplicativos.
- Se você tiver acesso a vários locatários, use o ícone de Configurações no menu superior para alternar para o locatário no qual deseja registrar o aplicativo no menu Diretórios + assinaturas.
- Navegue até Identidade>Aplicativos>Registros do aplicativo.
- Selecione Novo registro.
- Insira um Nome para seu aplicativo. Os usuários do seu aplicativo podem ver esse nome e você pode alterá-lo mais tarde.
- Selecione Contas em qualquer diretório organizacional (Qualquer diretório do Microsoft Entra – Multilocatário) e contas Microsoft pessoais (por exemplo, Skype, Xbox) em Tipos de conta com suporte.
- Selecione Registrar.
- Em Gerenciar, selecione Autenticação>Adicionar uma plataforma>iOS/macOS.
- Insira a ID do Pacote do seu projeto. Se o exemplo de código for baixado, a ID do Pacote será
com.microsoft.identitysample.MSALiOS
. Se estiver criando seu próprio projeto, selecione seu projeto no Xcode e abra a guia Geral. O identificador do pacote será exibido na seção Identidade. - Selecione Configurar e salve a Configuração da MSAL exibida na página Configuração da MSAL, de modo que você possa inseri-la quando configurar o aplicativo mais tarde.
- Selecione Concluído.
Adicionar MSAL
Escolha uma das seguintes maneiras de instalar a biblioteca MSAL em seu aplicativo:
CocoaPods
Se estiver usando o CocoaPods, instale
MSAL
primeiro criando um arquivo vazio chamado podfile na mesma pasta do arquivo .xcodeproj do projeto. Adicione o seguinte ao arquivo Podfile:use_frameworks! target '<your-target-here>' do pod 'MSAL' end
Substitua
<your-target-here>
pelo nome do projeto.Em uma janela do terminal, navegue até a pasta que contém o arquivo podfile que você criou e execute
pod install
para instalar a biblioteca MSAL.Feche o Xcode e abra
<your project name>.xcworkspace
para recarregar o projeto no Xcode.
Carthage
Se estiver usando o Carthage, instale MSAL
adicionando-o ao arquivo 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 o 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 conferir a última versão e usá-la como uma estrutura em seu aplicativo.
Adicionar o registro do aplicativo.
Em seguida, adicionaremos o registro de aplicativo ao código.
Primeiro, adicione a seguinte instrução de importação à 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ê precisa modificar acima é que o valor atribuído a kClientID
seja a ID do Aplicativo. Esse valor faz parte dos dados de Configuração da MSAL que você salvou durante a etapa no início deste tutorial para registrar o aplicativo no portal do Azure.
Definir as configurações de projeto do Xcode
Adicione um novo grupo de conjunto de chaves ao seu projeto Autenticação e Funcionalidades. O grupo de conjunto de chaves deve ser com.microsoft.adalcache
no iOS e com.microsoft.identity.universalstorage
no macOS.
Somente para o iOS, Configure os esquemas de URL
Nesta etapa, você vai registrar CFBundleURLSchemes
para que o usuário possa ser redirecionado novamente ao aplicativo após a entrada. A propósito, LSApplicationQueriesSchemes
também permite que seu aplicativo faça uso do Microsoft Authenticator.
No Xcode, abra o arquivo Info.plist como arquivo de código-fonte e adicione o código a seguir dentro da seção <dict>
. Substitua [BUNDLE_ID]
pelo valor usado anteriormente. Se você baixou o código, o identificador do pacote é com.microsoft.identitysample.MSALiOS
. Se estiver criando seu próprio projeto, selecione seu projeto no Xcode e abra a guia Geral. O identificador do pacote será exibido 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>
Somente para macOS, configure a área restrita do aplicativo
- Acesse as Configurações do Projeto do Xcode >guia Funcionalidades>Área Restrita do Aplicativo
- Marque a caixa de seleção Conexões de Saída (Cliente) .
Criar a interface do usuário do aplicativo
Agora crie uma interface do usuário que inclui um botão para chamar a API do Microsoft Graph, outra para sair e uma exibição de texto para ver uma saída adicionando o seguinte código à classe ViewController
:
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 classe ViewController
, substitua o método viewDidLoad()
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 a MSAL
Inicializar a MSAL
Para a classe ViewController
, adicione o método initMSAL
:
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 ViewController
e depois do método initMSAL
, adicione o método initWebViewParams
:
Código do iOS:
func initWebViewParams() {
self.webViewParameters = MSALWebviewParameters(authPresentationViewController: self)
}
Código do macOS:
func initWebViewParams() {
self.webViewParameters = MSALWebviewParameters()
}
Gerenciar o retorno de chamada de entrada (somente iOS)
Abra o arquivo AppDelegate.swift. Para manipular o retorno de chamada após a entrada, adicione MSALPublicClientApplication.handleMSALResponse
à classe appDelegate
desta 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 estiver usando o Xcode 11, você deve colocar o retorno de chamada da MSAL em SceneDelegate.swift. Se você der suporte a UISceneDelegate e UIApplicationDelegate para compatibilidade com o iOS mais antigo, o retorno de chamada da MSAL precisará ser colocado nos dois 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)
}
Adquirir tokens
Agora, podemos implementar a lógica de processamento da interface do usuário do aplicativo e obter tokens de maneira interativa por meio da MSAL.
A MSAL expõe dois métodos principais para obter tokens: acquireTokenSilently()
e acquireTokenInteractively()
.
acquireTokenSilently()
tenta conectar um usuário e obter tokens sem nenhuma interação do usuário, desde que uma conta esteja presente.acquireTokenSilently()
requer umMSALAccount
válido, que pode ser recuperado usando uma das APIs de enumeração de conta da MSAL. Este tutorial usaapplicationContext.getCurrentAccount(with: msalParameters, completionBlock: {})
para recuperar a conta atual.acquireTokenInteractively()
sempre mostra a interface do usuário ao tentar conectar o usuário. Ele pode usar os cookies de sessão no navegador ou uma conta no Microsoft Authenticator para fornecer uma experiência de SSO interativo.
Adicione o código a seguir à classe ViewController
:
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)
}
})
}
Obter um token interativamente
O snippet de código a seguir obtém um token pela primeira vez criando um objeto MSALInteractiveTokenParameters
e chamando acquireToken
. Em seguida, adicione o código que:
- Cria
MSALInteractiveTokenParameters
com escopos. - Chama
acquireToken()
com os parâmetros criados. - Trata os erros. Para obter mais detalhes, confira o Guia de tratamento de erro da MSAL para iOS e macOS.
- Trata o caso de sucesso.
Adicione o código a seguir à 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 propriedade promptType
de MSALInteractiveTokenParameters
configura o comportamento das solicitação de autenticação e de consentimento. Os seguintes valores têm suporte:
.promptIfNecessary
(padrão) – o usuário é solicitado somente se necessário. A experiência de SSO é determinada pela presença de cookies no modo de exibiçã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. Esse é o comportamento padrão..selectAccount
– se nenhum usuário for especificado, o modo de exibição da Web da autenticação apresentará uma lista de contas conectadas para o usuário selecionar..login
– requer que o usuário autentique no modo de exibição da Web. Somente uma conta poderá ser conectada por vez se você especificar esse valor..consent
– requer que o usuário dê consentimento ao conjunto atual de escopos da solicitação.
Obter um token no modo silencioso
Para adquirir um token atualizado silenciosamente, adicione o código a seguir à classe ViewController
. Ele cria um objeto MSALSilentTokenParameters
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 que você obtiver um token, seu aplicativo poderá usá-lo no cabeçalho HTTP para fazer uma solicitação autorizada ao Microsoft Graph:
chave de cabeçalho | value |
---|---|
Autorização | Bearer <token-de-acesso> |
Adicione o código a seguir à classe ViewController
:
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()
}
Confira API do Microsoft Graph para saber mais sobre a API do Microsoft Graph.
Usar a MSAL para saída
Em seguida, adicione o suporte para saída.
Importante
Sair com a MSAL remove todas as informações conhecidas sobre um usuário do aplicativo, bem como remove uma sessão ativa no dispositivo do usuário quando permitido pela configuração do dispositivo. Você também pode opcionalmente desconectar o usuário do navegador.
Para adicionar a funcionalidade de saída, adicione o código a seguir dentro da classe ViewController
.
@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)
})
}
}
Ativar cache de token
Por padrão, a MSAL armazena em cache os tokens do seu aplicativo nos conjuntos de chaves do iOS ou macOS.
Para habilitar o cache de token:
- Verifique se seu aplicativo está assinado corretamente
- Acesse as Configurações do projeto do Xcode >guia Funcionalidades>Habilitar o compartilhamento do conjunto de chaves
- Selecione + e insira um dos seguintes Grupos de Conjunto de Chaves:
- iOS:
com.microsoft.adalcache
- macOS -
com.microsoft.identity.universalstorage
- iOS:
Adicionar métodos auxiliares
Adicione os seguintes métodos auxiliares à classe ViewController
para concluir a amostra.
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)
}
Somente para iOS: obtém informações adicionais do dispositivo
Use o seguinte código 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.")
}
}
Aplicativos de várias contas
Este aplicativo foi criado para um cenário de conta única. A MSAL também dá suporte a cenários de várias contas, mas requer mais trabalho dos aplicativos. Você precisará criar a interface do usuário para ajudar a selecionar qual conta deseja usar para cada ação que requer tokens do usuário. Como alternativa, seu aplicativo pode implementar uma heurística para selecionar qual conta usar consultando todas as contas da MSAL. Por exemplo, confira API accountsFromDeviceForParameters:completionBlock:
Testar seu aplicativo
Compilar e implantar o aplicativo em um simulador ou dispositivo de teste. Você deve conseguir entrar e obter tokens para o Microsoft Entra ID ou contas Microsoft pessoais.
Na primeira vez que um usuário entrar no aplicativo, ele será solicitado pela identidade da Microsoft a fornecer consentimento às permissões solicitadas. Embora a maioria dos usuários consiga fornecer seu consentimento, alguns locatários do Microsoft Entra desabilitaram o consentimento do usuário, o que exige que os administradores forneçam consentimento em nome de todos os usuários. Para dar suporte a esse cenário, registre os escopos do seu aplicativo.
Após entrar, este aplicativo exibirá os dados retornados do ponto de extremidade /me
do Microsoft Graph.
Próximas etapas
Saiba mais sobre como criar aplicativos móveis que chamam APIs Web protegidas em nossa série de cenários com várias partes.