Tutorial: Anmelden von Benutzern und Aufrufen von Microsoft Graph aus einer iOS- oder macOS-App

In diesem Tutorial erstellen Sie eine iOS- oder macOS-App, die in die Microsoft Identity Platform eingebunden wird, um Benutzer anzumelden und ein Zugriffstoken abzurufen, mit dem die Microsoft Graph-API aufgerufen werden kann.

Am Ende dieses Tutorials wird Ihre Anwendung Anmeldungen von persönlichen Microsoft-Konten (einschließlich outlook.com, live.com und anderen) sowie von Geschäfts-, Schul- oder Unikonten von allen Unternehmen oder Organisationen akzeptieren, die Microsoft Entra ID nutzen. Dieses Tutorial gilt für iOS- und macOS-Apps. Bei diesen beiden Plattformen sind einige Schritte unterschiedlich.

Dieses Tutorial umfasst folgende Punkte:

  • Erstellen eines iOS- oder macOS-App-Projekts in Xcode
  • Registrieren der App im Microsoft Entra Admin Center
  • Hinzufügen von Code zur Unterstützung der Benutzeranmeldung und -abmeldung
  • Hinzufügen von Code zum Aufrufen der Microsoft Graph-API
  • Testen der App

Voraussetzungen

Funktionsweise der Tutorial-App

Screenshot davon, wie die in diesem Tutorial generierte Beispiel-App funktioniert.

Mit der App in diesem Tutorial können Benutzer angemeldet und Daten aus Microsoft Graph in deren Namen abgerufen werden. Auf diese Daten wird über eine geschützte API (hier: Microsoft Graph-API) zugegriffen, für die eine Autorisierung erforderlich ist und die über Microsoft Identity Platform geschützt ist.

Dies gilt insbesondere in folgenden Fällen:

  • Die App meldet den Benutzer entweder über einen Browser oder über Microsoft Authenticator an.
  • Der Endbenutzer akzeptiert die von Ihrer Anwendung angeforderten Berechtigungen.
  • Ihre App stellt ein Zugriffstoken für die Microsoft Graph-API aus.
  • Das Zugriffstoken ist in der HTTP-Anforderung an die Web-API enthalten.
  • Die Microsoft Graph-Antwort wird verarbeitet.

In diesem Beispiel wird die Microsoft-Authentifizierungsbibliothek (Microsoft Authentication Library, MSAL) zum Implementieren der Authentifizierung verwendet. MSAL erneuert Token automatisch, ermöglicht das einmalige Anmelden zwischen anderen Apps auf dem Gerät und verwaltet die Konten.

Wenn Sie eine vollständige Version der App herunterladen möchten, die Sie in diesem Tutorial erstellen, finden Sie beide Versionen auf GitHub:

Erstellen eines neuen Projekts

  1. Starten Sie Xcode, und wählen Sie Create a new Xcode project (Neues Xcode-Projekt erstellen) aus.
  2. Für iOS-Apps: Wählen Sie iOS>Single view App (Einzelansicht-App) und dann Next (Weiter) aus.
  3. Für macOS-Aps: Wählen Sie macOS>Cocoa-App und anschließend Next (Weiter) aus.
  4. Geben Sie einen Produktnamen ein.
  5. Legen Sie die Sprache auf Swift fest, und wählen Sie Next (Weiter) aus.
  6. Wählen Sie einen Ordner für das Erstellen Ihrer App und dann die Option Erstellen aus.

Registrieren der Anwendung

Tipp

Die Schritte in diesem Artikel können je nach dem Portal, mit dem Sie beginnen, geringfügig variieren.

  1. Melden Sie sich beim Microsoft Entra Admin Center mindestens mit der Rolle Anwendungsentwickler an.
  2. Wenn Sie Zugriff auf mehrere Mandanten haben, verwenden Sie das Symbol für Einstellungen im oberen Menü, um zum Mandanten zu wechseln, in dem Sie die Anwendung über das Menü Verzeichnisse + Abonnements registrieren möchten.
  3. Browsen Sie zu Identität>Anwendungen>App-Registrierungen.
  4. Wählen Sie Neue Registrierung aus.
  5. Geben Sie einen Namen für Ihre Anwendung ein. Benutzern Ihrer App wird wahrscheinlich dieser Namen angezeigt. Sie können ihn später ändern.
  6. Wählen Sie unter Unterstützte Kontotypen die Option Konten in allen Microsoft Entra-Verzeichnissen (beliebiges Microsoft Entra-Verzeichnis: mehrere Mandanten) und persönliche Microsoft-Konten (z. B. Skype, Xbox) aus.
  7. Wählen Sie Registrieren aus.
  8. Wählen Sie unter Verwalten die Optionen Authentifizierung>Plattform hinzufügen>iOS/macOS aus.
  9. Geben Sie die Bündel-ID Ihres Projekts ein. Wenn das Codebeispiel heruntergeladen wurde, lautet die Bundle-ID com.microsoft.identitysample.MSALiOS. Wählen Sie bei der Erstellung eines eigenen Projekts Ihr Projekt in Xcode aus, und öffnen Sie die Registerkarte General (Allgemein). Die Paket-ID wird im Abschnitt Identity (Identität) angezeigt.
  10. Wählen Sie Konfigurieren aus, und speichern Sie die MSAL-Konfiguration, die auf der Seite MSAL-Konfiguration angezeigt wird, damit Sie diese später beim Konfigurieren Ihrer App eingeben können.
  11. Wählen Sie Fertigaus.

Hinzufügen von MSAL

Installieren Sie MSAL mit einer der folgenden Optionen in Ihrer App:

CocoaPods

  1. Wenn Sie CocoaPods verwenden, installieren Sie MSAL. Erstellen Sie dazu zunächst in dem Ordner, in dem sich auch die Datei .xcodeproj Ihres Projekts befindet, eine leere Datei namens podfile. Fügen Sie der Datei podfile Folgendes hinzu:

    use_frameworks!
    
    target '<your-target-here>' do
       pod 'MSAL'
    end
    
  2. Ersetzen Sie <your-target-here> durch den Namen Ihres Projekts.

  3. Navigieren Sie in einem Terminalfenster zu dem Ordner, der die erstellte Datei podfile enthält, und führen Sie pod install zum Installieren der MSAL-Bibliothek aus.

  4. Schließen Sie Xcode, und öffnen Sie <your project name>.xcworkspace, um das Projekt erneut in Xcode zu laden.

Carthage

Wenn Sie Carthage verwenden, installieren Sie MSAL, indem Sie die Bibliothek Ihrer Cartfile-Datei hinzufügen:

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

Führen Sie in dem Verzeichnis, in dem sich auch die aktualisierte Cartfile-Datei befindet, in einem Terminalfenster den folgenden Befehl aus, damit Carthage die Abhängigkeiten in Ihrem Projekt aktualisiert.

iOS:

carthage update --platform iOS

macOS:

carthage update --platform macOS

Manuell

Sie können auch ein Git-Submodul oder das neueste Release als Framework in Ihrer Anwendung verwenden.

Datum der App-Registrierung

Im nächsten Schritt fügen Sie dem Code die App-Registrierung hinzu.

Fügen Sie zuerst oben in der Datei ViewController.swift die folgende Importanweisung und entweder AppDelegate.swift oder SceneDelegate.swift hinzu:

import MSAL

Fügen Sie als Nächstes den folgenden Code zu ViewController.swift vor viewDidLoad() hinzu:

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

Sie ändern nur den Wert, der kClientID als Anwendungs-ID zugewiesen wird. Dieser Wert ist Teil der MSAL-Konfigurationsdaten, die Sie am Anfang dieses Tutorials zum Registrieren der Anwendung gespeichert haben.

Konfigurieren von Xcode-Projekteinstellungen

Fügen Sie der Signierung und Funktionen Ihres Projekts eine neue Keychaingruppe hinzu. Die Keychaingruppe sollte unter iOS com.microsoft.adalcache und unter macOS com.microsoft.identity.universalstorage lauten.

Xcode-Benutzeroberfläche, die die empfohlene Einrichtung der Keychaingruppe zeigt.

Nur iOS: Konfigurieren von URL-Schemas

In diesem Schritt registrieren Sie CFBundleURLSchemes, damit der Benutzer nach der Anmeldung zurück zur App geleitet werden kann. LSApplicationQueriesSchemes ermöglicht Ihrer App darüber hinaus die Verwendung von Microsoft Authenticator.

Öffnen Sie in Xcode Info.plist als Quellcodedatei, und fügen Sie im Abschnitt <dict> Folgendes hinzu. Ersetzen Sie [BUNDLE_ID] durch den Wert, den Sie zuvor verwendet haben. Wenn Sie den Code heruntergeladen haben, lautet die Bundle-ID com.microsoft.identitysample.MSALiOS. Wählen Sie bei der Erstellung eines eigenen Projekts Ihr Projekt in Xcode aus, und öffnen Sie die Registerkarte General (Allgemein). Die Paket-ID wird im Abschnitt Identity (Identität) angezeigt.

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

Nur macOS: Konfigurieren der App-Sandbox

  1. Navigieren Sie zu „Xcode Projekteinstellungen“ >Registerkarte „Funktionen“>App-Sandbox.
  2. Aktivieren Sie das Kontrollkästchen Outgoing Connections (Client) (Ausgehende Verbindungen (Clients)).

Erstellen der Benutzeroberfläche Ihrer App

Erstellen Sie nun eine Benutzeroberfläche mit einer Schaltfläche zum Aufrufen der Microsoft Graph-API, einer Schaltfläche zum Abmelden und einer Textansicht zum Anzeigen von Ausgaben. Fügen Sie dazu der Klasse ViewController den folgenden Code hinzu:

iOS-Benutzeroberfläche

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

macOS-Benutzeroberfläche


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

Ersetzen Sie anschließend ebenfalls innerhalb der Klasse ViewController die Methode viewDidLoad() durch Folgendes:

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

Verwendung von MSAL

MSAL initialisieren

Fügen Sie der Klasse ViewController die Methode initMSAL hinzu:

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

Fügen Sie weiterhin in der Klasse ViewController und nach der Methode initMSAL die Methode initWebViewParams hinzu:

iOS-Code:

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

macOS-Code:

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

Behandeln des Anmelderückrufs (nur iOS)

Öffnen Sie die Datei AppDelegate.swift. Zur Verarbeitung des Rückrufs nach der Anmeldung fügen Sie MSALPublicClientApplication.handleMSALResponse wie folgt der Klasse appDelegate hinzu:

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

Bei Verwendung von Xcode 11 müssen Sie den MSAL-Rückruf stattdessen in der Datei SceneDelegate.swift anordnen. Wenn Sie zur Erzielung von Kompatibilität mit älteren iOS-Versionen sowohl „UISceneDelegate“ als auch „UIApplicationDelegate“ unterstützen, muss der MSAL-Rückruf in beide Dateien eingefügt werden.

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

Abrufen von Token

Jetzt können Sie die Verarbeitungslogik der Anwendungsbenutzeroberfläche implementieren und Token interaktiv über MSAL beziehen.

MSAL macht zwei primäre Methoden zum Abrufen von Token verfügbar: acquireTokenSilently() und acquireTokenInteractively().

  • acquireTokenSilently() versucht, einen Benutzer anzumelden und Token ohne Benutzerinteraktion abzurufen, solange ein Konto vorhanden ist. acquireTokenSilently() erfordert die Angabe eines gültigen MSALAccount, das mit einer der APIs für MSAL-Kontoenumeration abgerufen werden kann. In diesem Tutorial wird das aktuelle Konto mit applicationContext.getCurrentAccount(with: msalParameters, completionBlock: {}) abgerufen.

  • acquireTokenInteractively() zeigt stets die Benutzeroberfläche an, wenn versucht wird, den Benutzer anzumelden. Möglicherweise werden jedoch Sitzungscookies im Browser oder ein Konto in Microsoft Authenticator verwendet, um eine interaktive Anmeldung per SSO bereitzustellen.

Fügen Sie der ViewController-Klasse den folgenden Code hinzu:

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

Interaktives Abrufen eines Tokens

Der folgende Codeausschnitt ruft ein Token zum ersten Mal ab, indem ein MSALInteractiveTokenParameters-Objekt erstellt und acquireToken aufgerufen wird. Im nächsten Schritt fügen Sie Code für folgende Aktionen hinzu:

  1. Erstellen von MSALInteractiveTokenParameters mit Bereichen
  2. Aufrufen von acquireToken() mit den erstellten Parametern
  3. Behandeln von Fehlern. Weitere Informationen finden Sie im MSAL-Leitfaden zur Fehlerbehandlung für iOS und macOS.
  4. Verarbeiten der erfolgreichen Situation

Fügen Sie der ViewController -Klasse den folgenden Code hinzu.

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

Mit der promptType-Eigenschaft von MSALInteractiveTokenParameters werden die Authentifizierung und das Verhalten der Einwilligungsaufforderung konfiguriert. Die folgenden Werte werden unterstützt:

  • .promptIfNecessary (Standardwert): Die Aufforderung wird dem Benutzer nur bei Bedarf angezeigt. Die SSO-Benutzeroberfläche hängt vom Vorhandensein von Cookies in der Webansicht und vom Kontotyp ab. Wenn mehrere Benutzer angemeldet sind, wird die Kontoauswahl angezeigt. Dies ist das Standardverhalten.
  • .selectAccount: Wenn kein Benutzer angegeben ist, wird in der Webansicht für die Authentifizierung eine Liste mit den derzeit angemeldeten Konten angezeigt, zwischen denen der Benutzer wählen kann.
  • .login: Der Benutzer muss sich in der Webansicht authentifizieren. Wenn Sie diesen Wert angeben, kann nur jeweils ein Konto gleichzeitig angemeldet sein.
  • .consent: Der Benutzer muss seine Einwilligung zu den aktuellen Bereichen für die Anforderung erteilen.

Automatisches Abrufen eines Tokens

Fügen Sie der Klasse ViewController den folgenden Code hinzu, um automatisch ein aktualisiertes Token abzurufen. Ein MSALSilentTokenParameters-Objekt wird erstellt, und acquireTokenSilent() wird aufgerufen:


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

Aufrufen der Microsoft Graph-API

Sobald Sie ein Token erhalten haben, kann Ihre App es im HTTP-Header verwenden, um eine autorisierte Anforderung an Microsoft Graph zu stellen:

Headerschlüssel value
Authorization Bearer <acess-token>

Fügen Sie der ViewController-Klasse den folgenden Code hinzu:

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

Weitere Informationen zur Microsoft Graph-API finden Sie hier.

Verwenden von MSAL zur Abmeldung

Als Nächstes fügen Sie Unterstützung für die Abmeldung hinzu.

Wichtig

Die Abmeldung mit MSAL entfernt alle bekannten Informationen über einen Benutzer aus der Anwendung sowie eine aktive Sitzung auf seinem Gerät, wenn dies durch die Gerätekonfiguration zugelassen wird. Optional können Sie den Benutzer auch über den Browser abmelden.

Fügen Sie zum Hinzufügen einer Abmeldefunktion den folgenden Code in die ViewController-Klasse ein.

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

        }
    }

Aktivieren der Zwischenspeicherung von Token

MSAL speichert die Token Ihrer App standardmäßig in der iOS- oder macOS-Keychain zwischen.

So aktivieren Sie die Zwischenspeicherung von Token:

  1. Stellen Sie sicher, dass Ihre Anwendung ordnungsgemäß signiert ist.
  2. Navigieren Sie zu „Xcode-Projekteinstellungen“ >Registerkarte „Funktionen“>Keychain-Freigabe aktivieren.
  3. Wählen Sie + aus, und geben Sie eine der folgenden Keychain-Gruppen ein:
    • iOS: com.microsoft.adalcache
    • macOS: com.microsoft.identity.universalstorage

Hinzufügen von Hilfsmethoden

Fügen Sie der Klasse ViewController die folgenden Hilfsmethoden hinzu, um das Beispiel abzuschließen.

iOS-Benutzeroberfläche:


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

macOS-Benutzeroberfläche:

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

Nur iOS: Abrufen zusätzlicher Geräteinformationen

Verwenden Sie den folgenden Code zum Lesen der aktuellen Gerätekonfiguration, einschließlich Informationen dazu, ob das Gerät als freigegeben konfiguriert ist

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

Anwendungen mit mehreren Konten

Diese App ist für ein Szenario mit einem einzelnen Konto konzipiert. MSAL unterstützt auch Szenarien mit mehreren Konten, was jedoch Mehrarbeit bei der Anwendung erfordert. Sie müssen eine Benutzeroberfläche erstellen, mit der Benutzer das Konto auswählen können, das sie für jede Aktion, die Token erfordert, verwenden möchten. Als Alternative kann Ihre Anwendung eine Heuristik implementieren, um das zu verwendende Konto durch Abfragen aller Konten über MSAL auszuwählen. Siehe beispielsweise die API für accountsFromDeviceForParameters:completionBlock:

Testen Ihrer App

Erstellen Sie die App, und stellen Sie sie auf einem Testgerät oder in einem Simulator bereit. Sie sollten sich anmelden und Token für Microsoft Entra ID oder persönliche Microsoft-Konten abrufen können.

Wenn sich ein Benutzer zum ersten Mal bei Ihrer App anmeldet, wird er von Microsoft aufgefordert, den angeforderten Berechtigungen zuzustimmen. Die meisten Benutzer können zustimmen, allerdings haben einige Microsoft Entra-Mandanten die Zustimmung durch Benutzer deaktiviert, sodass Administratoren im Namen aller Benutzer zustimmen müssen. Zur Unterstützung dieses Szenarios müssen Sie die Bereiche Ihrer App registrieren.

Nach der Anmeldung zeigt die App die vom Microsoft Graph-Endpunkt /me zurückgegebenen Daten an.

Nächste Schritte

In der mehrteiligen Szenarioreihe erfahren Sie mehr über das Entwickeln von mobilen Apps, die geschützte Web-APIs abrufen.