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

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

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:

Tworzenie nowego projektu

  1. Otwórz program Xcode i wybierz pozycję Utwórz nowy projekt Xcode.
  2. W przypadku aplikacji systemu iOS wybierz pozycję Aplikacja z pojedynczym widokiem systemu iOS>, a następnie wybierz pozycję Dalej.
  3. W przypadku aplikacji systemu macOS wybierz pozycję Aplikacja Cocoa dla systemu macOS>, a następnie wybierz pozycję Dalej.
  4. Podaj nazwę produktu.
  5. Ustaw pozycję Język na Swift , a następnie wybierz pozycję Dalej.
  6. 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.

  1. Zaloguj się do centrum administracyjnego firmy Microsoft Entra co najmniej jako deweloper aplikacji.
  2. Jeśli masz dostęp do wielu dzierżaw, użyj ikonyUstawienia w górnym menu, aby przełączyć się do dzierżawy, w której chcesz zarejestrować aplikację z menu Katalogi i subskrypcje.
  3. Przejdź do aplikacji tożsamości>> Rejestracje aplikacji.
  4. Wybierz opcjęNowa rejestracja.
  5. Wprowadź nazwę aplikacji. Użytkownicy aplikacji mogą zobaczyć tę nazwę i możesz ją zmienić później.
  6. 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.
  7. Wybierz pozycję Zarejestruj.
  8. W obszarze Zarządzanie wybierz pozycję Uwierzytelnianie>Dodaj platformę>iOS/macOS.
  9. 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ść .
  10. 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.
  11. Wybierz pozycję Gotowe.

Dodawanie biblioteki MSAL

Wybierz jeden z następujących sposobów instalowania biblioteki MSAL w aplikacji:

CocoaPods

  1. 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
    
  2. Zastąp <your-target-here> ciąg nazwą projektu.

  3. W oknie terminalu przejdź do folderu zawierającego utworzony plik podfile i uruchom polecenie pod install , aby zainstalować bibliotekę MSAL.

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

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

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

  1. Przejdź do karty>Piaskownica aplikacji projektu Xcode Ustawienia >
  2. Zaznacz pole wyboru Wychodzące Połączenie ions (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łowego MSALAccountelementu , który można pobrać przy użyciu jednego z interfejsów API wyliczania konta biblioteki MSAL. W tym samouczku użyto applicationContext.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:

  1. Tworzy MSALInteractiveTokenParameters z zakresami.
  2. Wywołania acquireToken() z utworzonymi parametrami.
  3. 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.
  4. 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ę MSALSilentTokenParametersacquireTokenSilent():


    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:

  1. Upewnij się, że aplikacja jest prawidłowo podpisana
  2. Przejdź do karty>Możliwości Ustawienia >projektu Xcode Włącz udostępnianie łańcucha kluczy
  3. Wybierz + i wprowadź jedną z następujących grup łańcucha kluczy:
    • Ios: com.microsoft.adalcache
    • Macos: com.microsoft.identity.universalstorage

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.