Zelfstudie: Gebruikers aanmelden en Microsoft Graph aanroepen vanuit een iOS- of macOS-app

In deze zelfstudie bouwt u een iOS- of macOS-app die integreert met het Microsoft-identiteitsplatform voor het aanmelden van gebruikers. U krijgt een toegangstoken waarmee u de Microsoft Graph API kunt aanroepen.

Wanneer u de zelfstudie hebt voltooid, accepteert uw toepassing aanmeldingen van persoonlijke Microsoft-accounts (inclusief outlook.com, live.com en andere accounts) en werk- of schoolaccounts van een bedrijf of organisatie die gebruikmaakt van Microsoft Entra ID. Deze zelfstudie is van toepassing op iOS- en macOS-apps. Sommige stappen verschillen tussen de twee platforms.

In deze zelfstudie:

  • Een iOS- of macOS-app-project maken in Xcode
  • De app registreren in het Microsoft Entra-beheercentrum
  • Code toevoegen voor de ondersteuning van het aan- en afmelden van gebruikers
  • Code toevoegen om de Microsoft Graph API aan te roepen
  • De app testen

Vereisten

Hoe de zelfstudie-app werkt

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

Met de app in deze zelfstudie kunnen gebruikers zich aanmelden en gegevens ophalen vanuit Microsoft Graph. Deze gegevens worden geopend via een beveiligde API (Microsoft Graph API in dit geval) waarvoor autorisatie is vereist en die wordt beveiligd door het Microsoft Identity Platform.

Specifieke opdrachten:

  • Uw app meldt de gebruiker aan via een browser of microsoft Authenticator.
  • De eindgebruiker accepteert de machtigingen die uw toepassing heeft aangevraagd.
  • Uw app krijgt een toegangstoken voor de Microsoft Graph API.
  • Het toegangstoken is opgenomen in de HTTP-aanvraag voor de web-API.
  • Verwerk het Microsoft Graph-antwoord.

In dit voorbeeld wordt de MSAL (Microsoft Authentication Library) gebruikt om Authentication te implementeren. In MSAL worden tokens automatisch vernieuwd, eenmalige aanmelding (SSO) geboden tussen andere apps op het apparaat, en de accounts beheerd.

Als u een voltooide versie wilt downloaden van de app die u in deze zelfstudie hebt gemaakt, kunt u beide versies vinden op GitHub:

Een nieuw project maken

  1. Open Xcode en selecteer Een nieuw Xcode-project maken.
  2. Selecteer voor iOS-apps iOS>App met één weergave, en selecteer Volgende.
  3. Selecteer voor macOS-apps macOS>Cocoa -app ,en selecteer Volgende.
  4. Geef een projectnaam op.
  5. Stel de Taal in op Swift, en selecteer Volgende.
  6. Selecteer een map om daarin de app te maken en selecteer Maken.

Registreer de toepassing

Tip

Stappen in dit artikel kunnen enigszins variëren op basis van de portal waaruit u begint.

  1. Meld u als toepassingsontwikkelaar aan bij het Microsoft Entra-beheercentrum.
  2. Als u toegang hebt tot meerdere tenants, gebruikt u het pictogram Instellingen in het bovenste menu om over te schakelen naar de tenant waarin u de toepassing wilt registreren in het menu Mappen en abonnementen.
  3. Blader naar identiteitstoepassingen>> App-registraties.
  4. Selecteer Nieuwe registratie.
  5. Voer een Naam in voor de toepassing. Gebruikers van uw app kunnen de naam zien. U kunt deze later wijzigen.
  6. Selecteer Accounts in een organisatiedirectory (Elke Microsoft Entra-directory - Multitenant) en persoonlijke Microsoft-accounts (bijvoorbeeld Skype, Xbox) onder Ondersteunde accounttypen.
  7. Selecteer Registreren.
  8. Selecteer onder Beheren achtereenvolgens Verificatie>Een platform toevoegen>iOS/macOS.
  9. Voer de bundel-id van het project in. Als het codevoorbeeld is gedownload, is com.microsoft.identitysample.MSALiOS de bundel-id. Als u uw eigen project maakt, selecteert u uw project in Xcode en opent u het tabblad Algemeen . De bundel-id wordt weergegeven in de sectie Identiteit .
  10. Selecteer Configureren en sla de MSAL-configuratie op die wordt weergegeven op de pagina MSAL-configuratie, zodat u deze kunt invoeren wanneer u de app later configureert.
  11. Selecteer Gereed.

MSAL toevoegen

Kies een van de volgende manieren om de MSAL in uw app te installeren:

CocoaPods

  1. Als u CocoaPods gebruikt, installeert u MSAL door eerst een leeg bestand genaamd podfile te maken in dezelfde map als het .xcodeproj-bestand van uw project. Voeg het volgende aan podfile toe:

    use_frameworks!
    
    target '<your-target-here>' do
       pod 'MSAL'
    end
    
  2. Vervang <your-target-here> door de naam van uw project.

  3. Navigeer in een terminalvenster naar de map die het podfile bevat dat u hebt gemaakt, en voer pod install uit om de MSAL-bibliotheek te installeren.

  4. Sluit Xcode en open <your project name>.xcworkspace om het project opnieuw te laden in Xcode.

Carthage

Als u Carthagegebruikt, installeert u MSAL door het toe te voegen aan uw Cartfile:

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

Voer vanuit een terminalvenster, in dezelfde map als het bijgewerkte Cartfile, de volgende opdracht uit om de afhankelijkheden in uw project bij te werken met Carthage.

iOS:

carthage update --platform iOS

macOS:

carthage update --platform macOS

Handmatig

U kunt ook een Git-submodule gebruiken of de meest recente release bekijken om te gebruiken als framework in uw toepassing.

De app-registratie toevoegen

Vervolgens voegen we uw app-registratie toe aan uw code.

Voeg eerst de volgende importinstructie toe aan het begin van het bestand ViewController.swift en appDelegate.swift of SceneDelegate.swift:

import MSAL

Voeg daarna de volgende code toe aan ViewController.swift vóór 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?

De enige waarde die u wijzigt, is de waarde die is toegewezen aan kClientID uw toepassings-id. Deze waarde maakt deel uit van de MSAL-configuratiegegevens die u tijdens de stap aan het begin van deze zelfstudie hebt opgeslagen om de toepassing te registreren.

Xcode-projectinstellingen configureren

Voeg een nieuwe sleutelketengroep toe aan uw project Ondertekening en mogelijkheden. De sleutelketengroep moet zijn: com.microsoft.adalcache in iOS en com.microsoft.identity.universalstorage in macOS.

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

Alleen voor iOS: URL-schema's configureren

In deze stap registreert u CFBundleURLSchemes, zodat de gebruiker na aanmelding kan worden omgeleid naar de app. Met LSApplicationQueriesSchemes kan uw app bovendien ook gebruikmaken van Microsoft Authenticator.

Open Info.plist in Xcode als een broncodebestand, en voeg het volgende toe in de sectie <dict>. Vervang [BUNDLE_ID] door de waarde die u eerder hebt gebruikt. Als u de code hebt gedownload, is de bundel-id com.microsoft.identitysample.MSALiOS. Als u uw eigen project maakt, selecteert u uw project in Xcode en opent u het tabblad Algemeen . De bundel-id wordt weergegeven in de sectie Identiteit .

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

Alleen voor macOS: App-sandbox configureren

  1. Ga naar de instellingen voor uw Xcode-project >tabblad Mogelijkheden>App-sandbox
  2. Selecteer het selectie vakje Uitgaande verbindingen (client).

De gebruikersinterface van uw app maken

Maak nu een gebruikersinterface die een knop voor het aanroepen van de Microsoft Graph API, een knop voor afmelden, en een tekstweergave voor uitvoer bevat, door de volgende code toe te voegen aan de klasse ViewController:

iOS-gebruikersinterface

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


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

Vervang vervolgens in de klasse ViewController de methode viewDidLoad() door:

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

MSAL gebruiken

MSAL initialiseren

Voeg aan de klasse ViewController de methode initMSAL toe:

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

Voeg in de klasse ViewController en na de methode initMSAL de methode initWebViewParams toe:

iOS-code:

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

macOS-code:

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

De callback na aanmelding verwerken (alleen voor iOS)

Open het bestand AppDelegate.swift. Als u de callback na aanmelding wilt afhandelen, voegt u MSALPublicClientApplication.handleMSALResponse toe aan de klasse appDelegate, zoals:

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

Als u Xcode 11 gebruikt, moet u MSAL-callback in plaats hiervan in SceneDelegate.swift plaatsen. Als u ondersteuning biedt voor UISceneDelegate en UIApplicationDelegate voor compatibiliteit met oudere iOS-versies, moet MSAL-callback in beide bestanden worden geplaatst.

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

Tokens verkrijgen

Nu kunnen we verwerkingslogica voor de gebruikersinterface van de toepassing implementeren en interactief tokens ophalen via MSAL.

MSAL biedt twee primaire methoden voor het ophalen van tokens: acquireTokenSilently() en acquireTokenInteractively().

  • Met acquireTokenSilently() wordt geprobeerd om een gebruiker aan te melden en tokens op te halen zonder enige gebruikersinteractie, mits een account aanwezig is. acquireTokenSilently() vereist een geldige MSALAccount, die kan worden opgehaald met behulp van een van de opsommings-API's van MSAL. In deze zelfstudie wordt applicationContext.getCurrentAccount(with: msalParameters, completionBlock: {}) gebruikt om het huidige account op te halen.

  • Met acquireTokenInteractively() wordt altijd de gebruikersinterface weergegeven bij een poging om de gebruiker aan te melden. Voor een interactieve ervaring met eenmalige aanmelding kunnen sessiecookies in de browser worden gebruikt of een account in Microsoft Authenticator.

Voeg de volgende code toe aan de klasse 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)
            }
        })
    }

Interactief een token ophalen

Met het volgende codefragment wordt de eerste keer een token opgehaald door een object MSALInteractiveTokenParameters te maken en acquireToken aan te roepen. Vervolgens voegt u code toe die:

  1. MSALInteractiveTokenParameters maken met bereiken.
  2. acquireToken() aanroepen met de gemaakte parameters.
  3. Fouten afhandelen. Raadpleeg Handleiding voor foutafhandeling in MSAL voor iOS en macOS voor meer details.
  4. De geslaagde aanvraag afhandelen.

Voeg de volgende code toe aan de klasse 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()
    }
}

Met de eigenschap promptType van MSALInteractiveTokenParameters configureert u het gedrag voor verificatie en voor de instemmingsprompt. De volgende waarden worden ondersteund:

  • .promptIfNecessary (standaard): de gebruiker krijgt alleen een prompt te zien als dat nodig is. De SSO-ervaring wordt bepaald door de aanwezigheid van cookies in de webweergave en door het accounttype. Als er meerdere gebruikers zijn aangemeld, wordt de accountselectie weergegeven. Dit is het standaardgedrag.
  • .selectAccount: als er geen gebruiker is opgegeven, wordt in de webweergave voor verificatie een lijst weergegeven met de op dat moment aangemelde accounts waaruit de gebruiker een selectie kan maken.
  • .login: vereist dat de gebruiker zich verifieert in de webweergave. Er kan slechts één account tegelijk zijn aangemeld als u deze waarde opgeeft.
  • .consent: de gebruiker moet toestemming geven voor de huidige set scopes voor de aanvraag.

Een token op de achtergrond ophalen

Als u een bijgewerkt token op de achtergrond wilt ophalen, voegt u de volgende code toe aan de klasse ViewController. Hiermee maakt u een object MSALSilentTokenParameters en roept u acquireTokenSilent() aan:


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

De Microsoft Graph API aanroepen

Zodra u een token hebt, kan de app deze gebruiken in de HTTP-koptekst om een geautoriseerde aanvraag te doen bij Microsoft Graph:

sleutel voor koptekst waarde
Autorisatie Bearer-<toegangstoken>

Voeg de volgende code toe aan de klasse 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()
    }

Zie Microsoft Graph API voor meer informatie over de Microsoft Graph API.

MSAL gebruiken voor afmelden

Voeg vervolgens ondersteuning voor afmelden toe.

Belangrijk

Bij afmelden met MSAL wordt alle bekende informatie over een gebruiker verwijderd uit de toepassing. Ook wordt een actieve sessie verwijderd op het apparaat van de gebruiker wanneer dit is toegestaan op basis van de apparaatconfiguratie. U kunt de gebruiker ook afmelden vanuit de browser.

Als u de mogelijkheid voor afmelden wilt toevoegen, voegt u de volgende code toe binnen de klasse ViewController.

@objc func signOut(_ sender: AnyObject) {

        guard let applicationContext = self.applicationContext else { return }

        guard let account = self.currentAccount else { return }

        do {

            /**
             Removes all tokens from the cache for this application for the provided account

             - account:    The account to remove from the cache
             */

            let signoutParameters = MSALSignoutParameters(webviewParameters: self.webViewParameters!)
            signoutParameters.signoutFromBrowser = false // set this to true if you also want to signout from browser or webview

            applicationContext.signout(with: account, signoutParameters: signoutParameters, completionBlock: {(success, error) in

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

                self.updateLogging(text: "Sign out completed successfully")
                self.accessToken = ""
                self.updateCurrentAccount(account: nil)
            })

        }
    }

Tokencaching inschakelen

De tokens van uw app worden met MSAL standaard in de cache opgeslagen in de sleutelketen in iOS of macOS.

Tokencaching inschakelen:

  1. Zorg ervoor dat de toepassing juist is afgemeld
  2. Ga naar de instellingen voor uw Xcode-project >tabblad Mogelijkheden>Sleutelketens delen inschakelen
  3. Selecteer + en voer een van de volgende sleutelhangergroepen in:
    • iOS: com.microsoft.adalcache
    • macOS: com.microsoft.identity.universalstorage

Help-methoden toevoegen

Voeg de volgende Help-methode toe aan de klasse ViewController om het voorbeeld te voltooien.

iOS-gebruikersinterface:


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

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

Alleen voor iOS: aanvullende informatie over het apparaat ophalen

Gebruik de volgende code om de huidige apparaatconfiguratie te lezen, inclusief of het apparaat is geconfigureerd als gedeeld:

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

Toepassingen met meerdere accounts

Deze app is gebouwd voor een enkel accountscenario. MSAL biedt ook ondersteuning voor scenario's met meerdere accounts, maar hiervoor is extra werk voor apps vereist. U moet een gebruikersinterface maken om gebruikers te helpen bij het selecteren van het account dat ze willen gebruiken voor elke actie waarvoor tokens zijn vereist. Uw app kan ook een heuristiek implementeren om te selecteren welk account moet worden gebruikt, door alle account van MSAL te doorzoeken. Zie bijvoorbeeld accountsFromDeviceForParameters:completionBlock:API

Uw app testen

Bouw en implementeer de app op een testapparaat of simulator. U moet zich kunnen aanmelden en tokens ophalen voor Microsoft Entra ID of persoonlijke Microsoft-accounts.

De eerste keer dat een gebruiker zich aanmeldt bij uw app, wordt diegene door Microsoft gevraagd toestemming te geven voor de aangevraagde machtigingen. Hoewel de meeste gebruikers toestemming kunnen geven, hebben sommige Microsoft Entra-tenants gebruikerstoestemming uitgeschakeld. Hiervoor moeten beheerders toestemming geven namens alle gebruikers. Registreer de bereiken van uw app om dit scenario te ondersteunen.

Nadat u zich hebt aangemeld, verschijnen in de app de gegevens die zijn geretourneerd via het Microsoft Graph /me-eindpunt.

Volgende stappen

Meer informatie over het bouwen van mobiele apps die beveiligde web-API's aanroepen in deze reeks met meerdere scenario's.