Kurz: Přihlášení uživatelů a volání Microsoft Graphu z aplikace pro iOS nebo macOS

V tomto kurzu vytvoříte aplikaci pro iOS nebo macOS, která se integruje s platformou Microsoft Identity Platform pro podepisování uživatelů a získání přístupového tokenu pro volání rozhraní Microsoft Graph API.

Po dokončení kurzu vaše aplikace přijímá přihlášení k osobním účtům Microsoft (včetně outlook.com, live.com a dalších) a pracovních nebo školních účtů z libovolné společnosti nebo organizace, která používá ID Microsoft Entra. Tento kurz platí pro aplikace pro iOS i macOS. Některé kroky se mezi těmito dvěma platformami liší.

V tomto kurzu:

  • Vytvoření projektu aplikace pro iOS nebo macOS v Xcode
  • Registrace aplikace v Centru pro správu Microsoft Entra
  • Přidání kódu pro podporu přihlášení a odhlášení uživatele
  • Přidání kódu pro volání rozhraní Microsoft Graph API
  • Otestování aplikace

Požadavky

Jak aplikace tutorial funguje

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

Aplikace v tomto kurzu se může přihlásit uživatele a získat data z Microsoft Graphu jejich jménem. K datům se přistupuje prostřednictvím chráněného rozhraní API (v tomto případě rozhraní Microsoft Graph API), které vyžaduje autorizaci a je chráněno platformou Microsoft Identity Platform.

Konkrétně:

  • Vaše aplikace se přihlásí uživatele prostřednictvím prohlížeče nebo Microsoft Authenticatoru.
  • Koncový uživatel přijme oprávnění, která vaše aplikace požadovala.
  • Vaše aplikace je vystavena přístupový token pro rozhraní Microsoft Graph API.
  • Přístupový token je součástí požadavku HTTP na webové rozhraní API.
  • Zpracování odpovědi Microsoft Graphu

Tato ukázka používá knihovnu MSAL (Microsoft Authentication Library) k implementaci ověřování. MSAL automaticky prodlouží tokeny, zajistí jednotné přihlašování (SSO) mezi jinými aplikacemi na zařízení a spravuje účty.

Pokud si chcete stáhnout dokončenou verzi aplikace, kterou vytvoříte v tomto kurzu, najdete obě verze na GitHubu:

Vytvoření nového projektu

  1. Otevřete Xcode a vyberte Vytvořit nový projekt Xcode.
  2. U aplikací pro iOS vyberte aplikaci pro jedno zobrazení pro iOS>a vyberte Další.
  3. V případě aplikací pro macOS vyberte macOS>Cocoa App a vyberte Další.
  4. Zadejte název produktu.
  5. Nastavte jazyk na Swift a vyberte Další.
  6. Vyberte složku, do které chcete aplikaci vytvořit, a vyberte Vytvořit.

Registrace aplikace

Tip

Postup v tomto článku se může mírně lišit v závislosti na portálu, od který začínáte.

  1. Přihlaste se do Centra pro správu Microsoft Entra jako alespoň vývojář aplikací.
  2. Pokud máte přístup k více tenantům, pomocí ikony Nastavení v horní nabídce přepněte na tenanta, ve kterém chcete aplikaci zaregistrovat z nabídky Adresáře a předplatná.
  3. Přejděte k aplikacím> identit>Registrace aplikací.
  4. Vyberte Nová registrace.
  5. Zadejte název aplikace. Uživatelé vaší aplikace můžou vidět tento název a později ho můžete změnit.
  6. V části Podporované typy účtů vyberte účty v libovolném organizačním adresáři (jakýkoli adresář Microsoft Entra – Víceklient) a osobní účty Microsoft (např. Skype, Xbox).
  7. Vyberte Zaregistrovat.
  8. V části Spravovat vyberte Možnost Přidat ověřování>pro platformu>iOS/macOS.
  9. Zadejte ID sady prostředků projektu. Pokud jste si stáhli ukázku kódu, ID sady je com.microsoft.identitysample.MSALiOS. Pokud vytváříte vlastní projekt, vyberte projekt v Xcode a otevřete kartu Obecné . Identifikátor sady se zobrazí v části Identita .
  10. Vyberte Konfigurovat a uložte konfiguraci MSAL, která se zobrazí na stránce konfigurace MSAL, abyste ji mohli zadat při pozdější konfiguraci aplikace.
  11. Vyberte Hotovo.

Přidání knihovny MSAL

Zvolte jeden z následujících způsobů instalace knihovny MSAL ve vaší aplikaci:

CocoaPods

  1. Pokud používáte CocoaPods, nainstalujte nejprve MSAL prázdný soubor s názvem podfile ve stejné složce jako soubor .xcodeproj vašeho projektu. Do souboru podfile přidejte následující položky:

    use_frameworks!
    
    target '<your-target-here>' do
       pod 'MSAL'
    end
    
  2. Nahraďte <your-target-here> názvem projektu.

  3. V okně terminálu přejděte do složky, která obsahuje soubor podfile , který jste vytvořili, a spusťte pod install instalaci knihovny MSAL.

  4. Zavřete Xcode a otevřete <your project name>.xcworkspace ho, aby se projekt znovu načítá v Xcode.

Kartágo

Pokud používáte Carthage, nainstalujte MSAL ho přidáním do souboru Cartfile:

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

V okně terminálu ve stejném adresáři jako aktualizovaný cartfile spusťte následující příkaz, aby Carthage aktualizoval závislosti v projektu.

Ios:

carthage update --platform iOS

macOS:

carthage update --platform macOS

Ručně

Můžete také použít Git Submodule nebo si prohlédnout nejnovější verzi, která se má použít jako architektura ve vaší aplikaci.

Přidání registrace aplikace

Dále přidáme registraci vaší aplikace do kódu.

Nejprve přidejte následující příkaz importu do horní části souboru ViewController.swift a buď AppDelegate.swift , nebo SceneDelegate.swift:

import MSAL

Dále přidejte následující kód do ViewController.swift před: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?

Jedinou hodnotou, kterou upravíte, je hodnota přiřazená kClientID jako ID vaší aplikace. Tato hodnota je součástí konfiguračních dat MSAL, která jste uložili během kroku na začátku tohoto kurzu pro registraci aplikace.

Konfigurace nastavení projektu Xcode

Přidejte novou skupinu řetězce klíčů do možností podepisování a funkcí projektu. Skupina řetězce klíčů by měla být com.microsoft.adalcache v iOSu a com.microsoft.identity.universalstorage v macOS.

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

Pouze pro iOS nakonfigurujte schémata adres URL.

V tomto kroku se zaregistrujete CFBundleURLSchemes , aby se uživatel po přihlášení mohl přesměrovat zpět do aplikace. Mimochodem, LSApplicationQueriesSchemes umožňuje vaší aplikaci používat Microsoft Authenticator.

V Xcode otevřete Soubor Info.plist jako soubor zdrojového <dict> kódu a do části přidejte následující kód. Nahraďte [BUNDLE_ID] hodnotou, kterou jste použili dříve. Pokud jste kód stáhli, identifikátor sady je com.microsoft.identitysample.MSALiOS. Pokud vytváříte vlastní projekt, vyberte projekt v Xcode a otevřete kartu Obecné . Identifikátor sady se zobrazí v části Identita .

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

Pouze pro macOS nakonfigurujte Sandbox aplikace.

  1. Přejděte na kartu>Možnosti projektu Xcode Nastavení >App Sandbox
  2. Zaškrtněte políčko Odchozí Připojení iony (klient).

Vytvoření uživatelského rozhraní aplikace

Teď vytvořte uživatelské rozhraní, které obsahuje tlačítko pro volání rozhraní Microsoft Graph API, jiného pro odhlášení a textové zobrazení, které zobrazí nějaký výstup přidáním následujícího kódu do ViewController třídy:

Uživatelské rozhraní 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()
}

uživatelské rozhraní 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() {}

Dále v rámci ViewController třídy nahraďte metodu viewDidLoad() tímto:

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

Použití knihovny MSAL

Inicializace knihovny MSAL

ViewController Do třídy přidejte metoduinitMSAL:

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

Stále ve ViewController třídě a za metodou initMSAL přidejte metodu initWebViewParams :

Kód iOS:

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

Kód macOS:

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

Zpracování zpětného volání přihlášení (jenom iOS)

Otevřete soubor AppDelegate.swift. Chcete-li zpracovat zpětné volání po přihlášení, přidejte MSALPublicClientApplication.handleMSALResponse do appDelegate třídy takto:

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

Pokud používáte Xcode 11, měli byste místo toho umístit zpětné volání MSAL do sceneDelegate.swift . Pokud podporujete JAK UISceneDelegate, tak UIApplicationDelegate kvůli kompatibilitě se starším iOSem, bude nutné zpětné volání MSAL umístit do obou souborů.

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

Získání tokenů

Teď můžeme implementovat logiku zpracování uživatelského rozhraní aplikace a interaktivně získat tokeny prostřednictvím knihovny MSAL.

MSAL zveřejňuje dvě primární metody pro získání tokenů: acquireTokenSilently() a acquireTokenInteractively().

  • acquireTokenSilently() pokusí se přihlásit uživatele a získat tokeny bez zásahu uživatele, pokud je k dispozici účet. acquireTokenSilently() vyžadovat platnou MSALAccounthodnotu, kterou lze načíst pomocí některého z rozhraní API výčtu účtů MSAL. Tento kurz používá applicationContext.getCurrentAccount(with: msalParameters, completionBlock: {}) k načtení aktuálního účtu.

  • acquireTokenInteractively() při pokusu o přihlášení uživatele se vždy zobrazí uživatelské rozhraní. K zajištění interaktivního jednotného přihlašování může používat soubory cookie relace v prohlížeči nebo účet v authenticatoru Microsoftu.

Do třídy přidejte následující kód ViewController :

    func getGraphEndpoint() -> String {
        return kGraphEndpoint.hasSuffix("/") ? (kGraphEndpoint + "v1.0/me/") : (kGraphEndpoint + "/v1.0/me/");
    }

    @objc func callGraphAPI(_ sender: AnyObject) {

        self.loadCurrentAccount { (account) in

            guard let currentAccount = account else {

                // We check to see if we have a current logged in account.
                // If we don't, then we need to sign someone in.
                self.acquireTokenInteractively()
                return
            }

            self.acquireTokenSilently(currentAccount)
        }
    }

    typealias AccountCompletion = (MSALAccount?) -> Void

    func loadCurrentAccount(completion: AccountCompletion? = nil) {

        guard let applicationContext = self.applicationContext else { return }

        let msalParameters = MSALParameters()
        msalParameters.completionBlockQueue = DispatchQueue.main

        applicationContext.getCurrentAccount(with: msalParameters, completionBlock: { (currentAccount, previousAccount, error) in

            if let error = error {
                self.updateLogging(text: "Couldn't query current account with error: \(error)")
                return
            }

            if let currentAccount = currentAccount {

                self.updateLogging(text: "Found a signed in account \(String(describing: currentAccount.username)). Updating data for that account...")

                self.updateCurrentAccount(account: currentAccount)

                if let completion = completion {
                    completion(self.currentAccount)
                }

                return
            }

            self.updateLogging(text: "Account signed out. Updating UX")
            self.accessToken = ""
            self.updateCurrentAccount(account: nil)

            if let completion = completion {
                completion(nil)
            }
        })
    }

Interaktivní získání tokenu

Následující fragment kódu získá token poprvé vytvořením objektu MSALInteractiveTokenParameters a voláním acquireToken. Dále přidáte kód, který:

  1. Vytvoří MSALInteractiveTokenParameters s obory.
  2. Volání acquireToken() s vytvořenými parametry.
  3. Zpracovává chyby. Další podrobnosti najdete v průvodci zpracováním chyb MSAL pro iOS a macOS.
  4. Zpracuje úspěšný případ.

Do třídy ViewController přidejte následující kód.

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

Vlastnost promptTypeMSALInteractiveTokenParameters konfiguruje chování ověřování a výzvy k vyjádření souhlasu. Podporovány jsou následující hodnoty:

  • .promptIfNecessary (výchozí) – Uživatel se zobrazí pouze v případě potřeby. Prostředí jednotného přihlašování je určeno přítomností souborů cookie ve webovém zobrazení a typem účtu. Pokud je přihlášeno více uživatelů, zobrazí se prostředí pro výběr účtu. Toto je výchozí chování.
  • .selectAccount – Pokud není zadaný žádný uživatel, zobrazí ověřovací webové zobrazení seznam aktuálně přihlášených účtů, ze které má uživatel vybírat.
  • .login – Vyžaduje, aby se uživatel ověřil ve webovém zobrazení. Pokud tuto hodnotu zadáte, může být současně přihlášen pouze jeden účet.
  • .consent – Vyžaduje, aby uživatel souhlasil s aktuální sadou oborů žádosti.

Bezobslužné získání tokenu

Pokud chcete získat aktualizovaný token bezobslužně, přidejte do ViewController třídy následující kód. MSALSilentTokenParameters Vytvoří objekt a volání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()
        }
    }

Volání rozhraní Microsoft Graph API

Jakmile máte token, může ji vaše aplikace použít v hlavičce HTTP k vytvoření autorizovaného požadavku do Microsoft Graphu:

klíč záhlaví hodnota
Autorizace Nosný <přístupový token>

Do třídy přidejte následující kód ViewController :

    func getContentWithToken() {

        // Specify the Graph API endpoint
        let graphURI = getGraphEndpoint()
        let url = URL(string: graphURI)
        var request = URLRequest(url: url!)

        // Set the Authorization header for the request. We use Bearer tokens, so we specify Bearer + the token we got from the result
        request.setValue("Bearer \(self.accessToken)", forHTTPHeaderField: "Authorization")

        URLSession.shared.dataTask(with: request) { data, response, error in

            if let error = error {
                self.updateLogging(text: "Couldn't get graph result: \(error)")
                return
            }

            guard let result = try? JSONSerialization.jsonObject(with: data!, options: []) else {

                self.updateLogging(text: "Couldn't deserialize result JSON")
                return
            }

            self.updateLogging(text: "Result from Graph: \(result))")

            }.resume()
    }

Další informace o rozhraní Microsoft Graph API najdete v rozhraní Microsoft Graph API.

Použití knihovny MSAL k odhlášení

Dále přidejte podporu pro odhlášení.

Důležité

Odhlášení pomocí knihovny MSAL odebere všechny známé informace o uživateli z aplikace a také odebere aktivní relaci na svém zařízení, pokud je povolená konfigurace zařízení. Uživatele můžete také volitelně odhlásit z prohlížeče.

Pokud chcete přidat funkci odhlášení, přidejte do ViewController třídy následující kód.

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

        }
    }

Povolení ukládání tokenů do mezipaměti

Ve výchozím nastavení MSAL ukládá tokeny vaší aplikace do mezipaměti v klíčence pro iOS nebo macOS.

Povolení ukládání tokenů do mezipaměti:

  1. Ujistěte se, že je vaše aplikace správně podepsaná.
  2. Přejděte na kartu>Možnosti projektu Xcode Nastavení >Povolit sdílení řetězce klíčů.
  3. Vyberte + a zadejte jednu z následujících skupin řetězce klíčů:
    • Ios: com.microsoft.adalcache
    • Macos: com.microsoft.identity.universalstorage

Přidání pomocných metod

Přidejte do ViewController třídy následující pomocné metody pro dokončení ukázky.

Uživatelské rozhraní 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)
    }

uživatelské rozhraní 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)
    }

Jenom iOS: získání dalších informací o zařízeních

Pomocí následujícího kódu si můžete přečíst aktuální konfiguraci zařízení, včetně toho, jestli je zařízení nakonfigurované jako sdílené:

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

Vícefaktorové aplikace

Tato aplikace je vytvořená pro scénář jednoho účtu. MSAL také podporuje scénáře s více účty, ale vyžaduje více práce s aplikací. Musíte vytvořit uživatelské rozhraní, které uživatelům pomůže vybrat účet, který chtějí použít pro každou akci, která vyžaduje tokeny. Alternativně může vaše aplikace implementovat heuristiku pro výběr účtu, který se má použít, dotazováním všech účtů z KNIHOVNY MSAL. Podívejte se accountsFromDeviceForParameters:completionBlock:například na rozhraní API.

Testování aplikace

Sestavte a nasaďte aplikaci do testovacího zařízení nebo simulátoru. Měli byste být schopni se přihlásit a získat tokeny pro účty Microsoft Entra ID nebo osobní účty Microsoft.

Když se uživatel poprvé přihlásí k vaší aplikaci, vyzve ho identita Microsoftu k vyjádření souhlasu s požadovanými oprávněními. Většina uživatelů sice může souhlasit, ale někteří tenanti Microsoft Entra zakázali souhlas uživatele, což vyžaduje, aby správci souhlasili jménem všech uživatelů. Pokud chcete tento scénář podporovat, zaregistrujte rozsahy vaší aplikace.

Po přihlášení aplikace zobrazí data vrácená z koncového bodu Microsoft Graphu /me .

Další kroky

Přečtěte si další informace o vytváření mobilních aplikací, které volají chráněná webová rozhraní API v naší řadě scénářů s více částmi.