Samouczek: logowanie użytkowników i wywoływanie programu Microsoft Graph z poziomu aplikacji dla systemu iOS lub macOS
W tym samouczku utworzysz aplikację dla systemu iOS lub macOS zintegrowaną z Platforma tożsamości Microsoft w celu podpisania użytkowników i uzyskania tokenu dostępu w celu wywołania interfejsu API programu Microsoft Graph.
Po ukończeniu samouczka aplikacja akceptuje logowania osobistych kont Microsoft (w tym outlook.com, live.com i innych) oraz kont służbowych z dowolnej firmy lub organizacji korzystającej z identyfikatora Microsoft Entra. Ten samouczek ma zastosowanie zarówno do aplikacji systemu iOS, jak i macOS. Niektóre kroki różnią się między dwiema platformami.
W tym samouczku:
- Tworzenie projektu aplikacji systemu iOS lub macOS w środowisku Xcode
- Rejestrowanie aplikacji w centrum administracyjnym firmy Microsoft Entra
- Dodawanie kodu do obsługi logowania użytkownika i wylogowywanie się
- Dodawanie kodu w celu wywołania interfejsu API programu Microsoft Graph
- Testowanie aplikacji
Wymagania wstępne
Jak działa aplikacja samouczka
Aplikacja w tym samouczku może logować użytkowników i pobierać dane z programu Microsoft Graph w ich imieniu. Te dane są dostępne za pośrednictwem chronionego interfejsu API (w tym przypadku interfejsu API programu Microsoft Graph), który wymaga autoryzacji i jest chroniony przez Platforma tożsamości Microsoft.
W szczególności:
- Aplikacja loguje się do użytkownika za pośrednictwem przeglądarki lub aplikacji Microsoft Authenticator.
- Użytkownik końcowy akceptuje żądane uprawnienia aplikacji.
- Twoja aplikacja jest wystawiana token dostępu dla interfejsu API programu Microsoft Graph.
- Token dostępu jest uwzględniony w żądaniu HTTP do internetowego interfejsu API.
- Przetwarzanie odpowiedzi programu Microsoft Graph.
W tym przykładzie użyto biblioteki Microsoft Authentication Library (MSAL) do zaimplementowania uwierzytelniania. Biblioteka MSAL automatycznie odnawia tokeny, dostarcza logowanie jednokrotne między innymi aplikacjami na urządzeniu i zarządza kontami.
Jeśli chcesz pobrać ukończoną wersję aplikacji utworzonej w tym samouczku, możesz znaleźć obie wersje w witrynie GitHub:
- Przykładowy kod systemu iOS (GitHub)
- Przykładowy kod systemu macOS (GitHub)
Tworzenie nowego projektu
- Otwórz program Xcode i wybierz pozycję Utwórz nowy projekt Xcode.
- W przypadku aplikacji systemu iOS wybierz pozycję Aplikacja z pojedynczym widokiem systemu iOS>, a następnie wybierz pozycję Dalej.
- W przypadku aplikacji systemu macOS wybierz pozycję Aplikacja Cocoa dla systemu macOS>, a następnie wybierz pozycję Dalej.
- Podaj nazwę produktu.
- Ustaw pozycję Język na Swift , a następnie wybierz pozycję Dalej.
- Wybierz folder, aby utworzyć aplikację, a następnie wybierz pozycję Utwórz.
Rejestrowanie aplikacji
Napiwek
Kroki opisane w tym artykule mogą się nieznacznie różnić w zależności od portalu, od którego zaczynasz.
- Zaloguj się do centrum administracyjnego firmy Microsoft Entra co najmniej jako deweloper aplikacji.
- Jeśli masz dostęp do wielu dzierżaw, użyj ikony Ustawienia w górnym menu, aby przełączyć się do dzierżawy, w której chcesz zarejestrować aplikację z menu Katalogi i subskrypcje.
- Przejdź do aplikacji tożsamości>> Rejestracje aplikacji.
- Wybierz opcjęNowa rejestracja.
- Wprowadź nazwę aplikacji. Użytkownicy aplikacji mogą zobaczyć tę nazwę i możesz ją zmienić później.
- Wybierz pozycję Konta w dowolnym katalogu organizacyjnym (dowolny katalog Microsoft Entra — multitenant) i osobiste konta Microsoft (np. Skype, Xbox) w obszarze Obsługiwane typy kont.
- Wybierz pozycję Zarejestruj.
- W obszarze Zarządzanie wybierz pozycję Uwierzytelnianie>Dodaj platformę>iOS/macOS.
- Wprowadź identyfikator pakietu projektu. Jeśli pobrano przykładowy kod, identyfikator pakietu to
com.microsoft.identitysample.MSALiOS
. Jeśli tworzysz własny projekt, wybierz projekt w środowisku Xcode i otwórz kartę Ogólne . Identyfikator pakietu zostanie wyświetlony w sekcji Tożsamość . - Wybierz pozycję Konfiguruj i zapisz konfigurację biblioteki MSAL wyświetlaną na stronie konfiguracji biblioteki MSAL, aby można było ją wprowadzić podczas późniejszego konfigurowania aplikacji.
- Wybierz pozycję Gotowe.
Dodawanie biblioteki MSAL
Wybierz jeden z następujących sposobów instalowania biblioteki MSAL w aplikacji:
CocoaPods
Jeśli używasz narzędzia CocoaPods, zainstaluj
MSAL
go, tworząc najpierw pusty plik o nazwie podfile w tym samym folderze co plik xcodeproj projektu. Dodaj następujące polecenie do pliku podfile:use_frameworks! target '<your-target-here>' do pod 'MSAL' end
Zastąp
<your-target-here>
ciąg nazwą projektu.W oknie terminalu przejdź do folderu zawierającego utworzony plik podfile i uruchom polecenie
pod install
, aby zainstalować bibliotekę MSAL.Zamknij program Xcode i otwórz plik
<your project name>.xcworkspace
, aby ponownie załadować projekt w środowisku Xcode.
Kartagina
Jeśli używasz narzędzia Carthage, zainstaluj go MSAL
, dodając go do pliku Cartfile:
github "AzureAD/microsoft-authentication-library-for-objc" "master"
W oknie terminalu w tym samym katalogu co zaktualizowany plik Cartfile uruchom następujące polecenie, aby zaktualizować zależności w projekcie.
iOS.
carthage update --platform iOS
macOS:
carthage update --platform macOS
Ręcznie
Możesz również użyć modułu podrzędnego Git lub zapoznać się z najnowszą wersją, aby użyć jej jako struktury w aplikacji.
Dodawanie rejestracji aplikacji
Następnie dodamy rejestrację aplikacji do kodu.
Najpierw dodaj następującą instrukcję import na początku pliku ViewController.swift i AppDelegate.swift lub SceneDelegate.swift:
import MSAL
Następnie dodaj następujący kod do pliku ViewController.swift przed poleceniem 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?
Jedyną wartością, którą modyfikujesz, jest wartość przypisana do kClientID
identyfikatora aplikacji. Ta wartość jest częścią danych konfiguracji biblioteki MSAL zapisanych na początku tego samouczka w celu zarejestrowania aplikacji.
Konfigurowanie ustawień projektu Xcode
Dodaj nową grupę pęku kluczy do projektu Podpisywanie i możliwości. Grupa pęku kluczy powinna znajdować się com.microsoft.adalcache
w systemach iOS i com.microsoft.identity.universalstorage
macOS.
Tylko w przypadku systemu iOS skonfiguruj schematy adresów URL
W tym kroku zarejestrujesz CFBundleURLSchemes
się, aby użytkownik mógł zostać przekierowany z powrotem do aplikacji po zalogowaniu. Dzięki temu LSApplicationQueriesSchemes
aplikacja może również korzystać z aplikacji Microsoft Authenticator.
W programie Xcode otwórz plik Info.plist jako plik kodu źródłowego i dodaj następujący kod w <dict>
sekcji . Zastąp [BUNDLE_ID]
wartość użytą wcześniej. Jeśli pobrano kod, identyfikator pakietu to com.microsoft.identitysample.MSALiOS
. Jeśli tworzysz własny projekt, wybierz projekt w środowisku Xcode i otwórz kartę Ogólne . Identyfikator pakietu zostanie wyświetlony w sekcji Tożsamość .
<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>
Tylko w przypadku systemu macOS skonfiguruj piaskownicę aplikacji
- Przejdź do karty Możliwości projektu >Xcode Na karcie>Piaskownica aplikacji
- Zaznacz pole wyboru Połączenia wychodzące (klient).
Tworzenie interfejsu użytkownika aplikacji
Teraz utwórz interfejs użytkownika, który zawiera przycisk do wywołania interfejsu API programu Microsoft Graph, innego do wylogowania i widoku tekstowego w celu wyświetlenia niektórych danych wyjściowych, dodając następujący kod do ViewController
klasy:
Interfejs użytkownika systemu 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()
}
Interfejs użytkownika systemu 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() {}
Następnie w ViewController
klasie zastąp metodę viewDidLoad()
:
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()
}
Korzystanie z biblioteki MSAL
Inicjowanie biblioteki MSAL
ViewController
W klasie dodaj metodę 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()
}
Nadal w ViewController
klasie i po metodzie initMSAL
dodaj metodę initWebViewParams
:
Kod systemu iOS:
func initWebViewParams() {
self.webViewParameters = MSALWebviewParameters(authPresentationViewController: self)
}
Kod systemu macOS:
func initWebViewParams() {
self.webViewParameters = MSALWebviewParameters()
}
Obsługa wywołania zwrotnego logowania (tylko system iOS)
Otwórz plik AppDelegate.swift. Aby obsłużyć wywołanie zwrotne po zalogowaniu, dodaj MSALPublicClientApplication.handleMSALResponse
do appDelegate
klasy w następujący sposób:
// 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)
}
Jeśli używasz środowiska Xcode 11, należy umieścić wywołanie zwrotne biblioteki MSAL w pliku SceneDelegate.swift . Jeśli w celu zapewnienia zgodności ze starszymi systemami iOS obsługiwane są zarówno interfejsy użytkownikaSceneDelegate, jak i UIApplicationDelegate, wywołanie zwrotne biblioteki MSAL musi zostać umieszczone w obu plikach.
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)
}
Uzyskiwanie tokenów
Teraz możemy zaimplementować logikę przetwarzania interfejsu użytkownika aplikacji i interaktywnie pobierać tokeny za pośrednictwem biblioteki MSAL.
Biblioteka MSAL udostępnia dwie podstawowe metody pobierania tokenów: acquireTokenSilently()
i acquireTokenInteractively()
.
acquireTokenSilently()
próbuje zalogować użytkownika i uzyskać tokeny bez interakcji z użytkownikiem, o ile konto jest obecne.acquireTokenSilently()
Wymagaj prawidłowegoMSALAccount
elementu , który można pobrać przy użyciu jednego z interfejsów API wyliczania konta biblioteki MSAL. W tym samouczku użytoapplicationContext.getCurrentAccount(with: msalParameters, completionBlock: {})
metody do pobrania bieżącego konta.acquireTokenInteractively()
zawsze wyświetla interfejs użytkownika podczas próby zalogowania użytkownika. Może używać plików cookie sesji w przeglądarce lub na koncie w aplikacji Microsoft Authenticator w celu zapewnienia interaktywnego środowiska logowania jednokrotnego.
Dodaj następujący kod do ViewController
klasy:
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)
}
})
}
Interakcyjne pobieranie tokenu
Poniższy fragment kodu pobiera token po raz pierwszy przez utworzenie MSALInteractiveTokenParameters
obiektu i wywołanie metody acquireToken
. Następnie dodasz kod, który:
- Tworzy
MSALInteractiveTokenParameters
z zakresami. - Wywołania
acquireToken()
z utworzonymi parametrami. - Obsługuje błędy. Aby uzyskać więcej informacji, zapoznaj się z przewodnikiem obsługi błędów biblioteki MSAL dla systemów iOS i macOS.
- Obsługuje pomyślny przypadek.
Dodaj poniższy kod do klasy 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()
}
}
Właściwość promptType
konfigurowania MSALInteractiveTokenParameters
zachowania uwierzytelniania i monitu o wyrażenie zgody. Obsługiwane są następujące wartości:
.promptIfNecessary
(ustawienie domyślne) — użytkownik jest monitowany tylko w razie potrzeby. Środowisko logowania jednokrotnego jest określane przez obecność plików cookie w widoku internetowym i typ konta. Jeśli wielu użytkowników jest zalogowanych, zostanie wyświetlone środowisko wyboru konta. Jest to zachowanie domyślne..selectAccount
— Jeśli żaden użytkownik nie zostanie określony, w widoku webview uwierzytelniania zostanie wyświetlona lista aktualnie zalogowanych kont dla użytkownika do wyboru..login
— Wymaga, aby użytkownik uwierzytelnił się w widoku internetowym. Tylko jedno konto może być zalogowane naraz, jeśli określisz tę wartość..consent
— Wymaga od użytkownika wyrażenia zgody na bieżący zestaw zakresów żądania.
Dyskretne uzyskiwanie tokenu
Aby uzyskać zaktualizowany token w trybie dyskretnym, dodaj następujący kod do ViewController
klasy . Tworzy obiekt i wywołuje metodę MSALSilentTokenParameters
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()
}
}
Wywoływanie interfejsu API programu Microsoft Graph
Po utworzeniu tokenu aplikacja może użyć jej w nagłówku HTTP, aby utworzyć autoryzowane żądanie do programu Microsoft Graph:
klucz nagłówka | wartość |
---|---|
Autoryzacja | <Token dostępu elementu nośnego> |
Dodaj następujący kod do ViewController
klasy:
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()
}
Zobacz Interfejs API programu Microsoft Graph, aby dowiedzieć się więcej na temat interfejsu API programu Microsoft Graph.
Korzystanie z biblioteki MSAL na potrzeby wylogowywanie
Następnie dodaj obsługę wylogowywanie.
Ważne
Wylogowywanie przy użyciu biblioteki MSAL usuwa wszystkie znane informacje o użytkowniku z aplikacji, a także usuwa aktywną sesję na urządzeniu, jeśli jest to dozwolone przez konfigurację urządzenia. Możesz również opcjonalnie wylogować użytkownika z przeglądarki.
Aby dodać możliwość wylogowywanie, dodaj następujący kod w ViewController
klasie .
@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)
})
}
}
Włączanie buforowania tokenów
Domyślnie biblioteka MSAL buforuje tokeny aplikacji w pęku kluczy systemu iOS lub macOS.
Aby włączyć buforowanie tokenów:
- Upewnij się, że aplikacja jest prawidłowo podpisana
- Przejdź do karty>Możliwości ustawień >projektu Xcode Włącz udostępnianie łańcucha kluczy
- Wybierz + i wprowadź jedną z następujących grup łańcucha kluczy:
- Ios:
com.microsoft.adalcache
- macOS:
com.microsoft.identity.universalstorage
- Ios:
Dodawanie metod pomocnika
Dodaj następujące metody pomocnicze do klasy, ViewController
aby ukończyć przykład.
Interfejs użytkownika systemu 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)
}
Interfejs użytkownika systemu 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)
}
Tylko system iOS: uzyskiwanie dodatkowych informacji o urządzeniu
Użyj następującego kodu, aby odczytać bieżącą konfigurację urządzenia, w tym określić, czy urządzenie jest skonfigurowane jako udostępnione:
@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.")
}
}
Aplikacje z wieloma kontami
Ta aplikacja jest tworzona na potrzeby scenariusza pojedynczego konta. Biblioteka MSAL obsługuje również scenariusze obejmujące wiele kont, ale wymaga więcej pracy aplikacji. Musisz utworzyć interfejs użytkownika, aby ułatwić użytkownikom wybór konta, którego chcą użyć dla każdej akcji wymagającej tokenów. Alternatywnie aplikacja może zaimplementować heurystyczną, aby wybrać, które konto ma być używane, wysyłając zapytanie do wszystkich kont z biblioteki MSAL. Zobacz na przykład accountsFromDeviceForParameters:completionBlock:
interfejs API
Przetestuj aplikację
Skompiluj i wdróż aplikację na urządzeniu testowym lub w symulatorze. Powinno być możliwe zalogowanie się i uzyskanie tokenów dla kont Microsoft Entra ID lub osobistych kont Microsoft.
Po pierwszym zalogowaniu się użytkownika do aplikacji zostanie wyświetlony monit o zgodę na żądane uprawnienia przez tożsamość firmy Microsoft. Większość użytkowników może wyrazić zgodę, ale niektóre dzierżawy firmy Microsoft Entra wyłączyły zgodę użytkownika, co wymaga zgody administratorów w imieniu wszystkich użytkowników. Aby obsługiwać ten scenariusz, zarejestruj zakresy aplikacji.
Po zalogowaniu aplikacja wyświetli dane zwrócone z punktu końcowego programu Microsoft Graph /me
.
Następne kroki
Dowiedz się więcej o tworzeniu aplikacji mobilnych, które nazywają chronione internetowe interfejsy API w naszej serii scenariuszy wieloczęściowych.