Självstudie: Logga in användare och anropa Microsoft Graph från en iOS- eller macOS-app

I den här självstudien skapar du en iOS- eller macOS-app som integreras med Microsofts identitetsplattform för att signera användare och få en åtkomsttoken för att anropa Microsoft Graph API.

När du har slutfört självstudien godkänner ditt program inloggningar med personliga Microsoft-konton (inklusive outlook.com, live.com och andra) och arbets- eller skolkonton från alla företag eller organisationer som använder Microsoft Entra-ID. Den här självstudien gäller för både iOS- och macOS-appar. Vissa steg skiljer sig mellan de två plattformarna.

I den här självstudien:

  • Skapa ett iOS- eller macOS-appprojekt i Xcode
  • Registrera appen i administrationscentret för Microsoft Entra
  • Lägga till kod för att stödja användarinloggning och utloggning
  • Lägga till kod för att anropa Microsoft Graph API
  • Testa appen

Förutsättningar

Så här fungerar självstudieappen

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

Appen i den här självstudien kan logga in användare och hämta data från Microsoft Graph för deras räkning. Dessa data nås via ett skyddat API (Microsoft Graph API i det här fallet) som kräver auktorisering och skyddas av Microsofts identitetsplattform.

Mer specifikt:

  • Appen loggar in användaren antingen via en webbläsare eller Microsoft Authenticator.
  • Slutanvändaren godkänner de behörigheter som programmet har begärt.
  • Din app utfärdas en åtkomsttoken för Microsoft Graph-API:et.
  • Åtkomsttoken ingår i HTTP-begäran till webb-API:et.
  • Bearbeta Microsoft Graph-svaret.

Det här exemplet använder Microsoft Authentication Library (MSAL) för att implementera autentisering. MSAL förnyar automatiskt token, levererar enkel inloggning (SSO) mellan andra appar på enheten och hanterar kontona.

Om du vill ladda ned en färdig version av appen som du skapar i den här självstudien kan du hitta båda versionerna på GitHub:

Skapa ett nytt projekt

  1. Öppna Xcode och välj Skapa ett nytt Xcode-projekt.
  2. För iOS-appar väljer du iOS>Enkel visningsapp och väljer Nästa.
  3. För macOS-appar väljer du macOS>Cocoa App och väljer Nästa.
  4. Ange ett produktnamn.
  5. Ange Språket till Swift och välj Nästa.
  6. Välj en mapp för att skapa din app och välj Skapa.

Registrera programmet

Dricks

Stegen i den här artikeln kan variera något beroende på vilken portal du börjar från.

  1. Logga in på administrationscentret för Microsoft Entra som minst programutvecklare.
  2. Om du har åtkomst till flera klienter använder du ikonen Inställningar på den översta menyn för att växla till den klientorganisation där du vill registrera programmet från menyn Kataloger + prenumerationer.
  3. Bläddra till Identitetsprogram>> Appregistreringar.
  4. Välj Ny registrering.
  5. Ange ett namn för ditt program. Användare av din app kan se det här namnet och du kan ändra det senare.
  6. Välj Konton i valfri organisationskatalog (Alla Microsoft Entra-kataloger – Multitenant) och personliga Microsoft-konton (t.ex. Skype, Xbox) under Kontotyper som stöds.
  7. Välj Registrera.
  8. Under Hantera väljer du Autentisering>Lägg till en plattforms-iOS>/macOS.
  9. Ange projektets paket-ID. Om du har laddat ned kodexemplet är com.microsoft.identitysample.MSALiOSpaket-ID : . Om du skapar ett eget projekt väljer du projektet i Xcode och öppnar fliken Allmänt . Paketidentifieraren visas i avsnittet Identitet .
  10. Välj Konfigurera och spara MSAL-konfigurationen som visas på MSAL-konfigurationssidan så att du kan ange den när du konfigurerar appen senare.
  11. Välj Klar.

Lägg till MSAL

Välj något av följande sätt att installera MSAL-biblioteket i din app:

CocoaPods

  1. Om du använder CocoaPods installerar MSAL du genom att först skapa en tom fil med namnet podfile i samma mapp som projektets .xcodeproj-fil. Lägg till följande i poddfile:

    use_frameworks!
    
    target '<your-target-here>' do
       pod 'MSAL'
    end
    
  2. Ersätt <your-target-here> med namnet på projektet.

  3. I ett terminalfönster navigerar du till mappen som innehåller poddfilen som du skapade och kör pod install för att installera MSAL-biblioteket.

  4. Stäng Xcode och öppna <your project name>.xcworkspace för att läsa in projektet igen i Xcode.

Karthago

Om du använder Carthage installerar MSAL du genom att lägga till det i din Cartfile:

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

Från ett terminalfönster kör du följande kommando i samma katalog som den uppdaterade Cartfile för att låta Carthage uppdatera beroendena i projektet.

iOS:

carthage update --platform iOS

macOS:

carthage update --platform macOS

Manuellt

Du kan också använda Git-undermodulen eller kolla in den senaste versionen för att använda som ett ramverk i ditt program.

Lägg till appregistreringen

Därefter lägger vi till din appregistrering i koden.

Lägg först till följande importinstruktion högst upp i filen ViewController.swift och antingen AppDelegate.swift eller SceneDelegate.swift:

import MSAL

Lägg sedan till följande kod i ViewController.swift före i 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?

Det enda värde som du ändrar är värdet som tilldelats till kClientID ditt program-ID. Det här värdet är en del av de MSAL-konfigurationsdata som du sparade under steget i början av den här självstudien för att registrera programmet.

Konfigurera Xcode-projektinställningar

Lägg till en ny nyckelringsgrupp i projektets signerings- och funktioner. Nyckelringsgruppen ska finnas com.microsoft.adalcache i iOS och com.microsoft.identity.universalstorage på macOS.

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

Endast för iOS konfigurerar du URL-scheman

I det här steget registrerar CFBundleURLSchemes du så att användaren kan omdirigeras tillbaka till appen efter inloggningen. Förresten, LSApplicationQueriesSchemes gör det också möjligt för din app att använda Microsoft Authenticator.

I Xcode öppnar du Info.plist som en källkodsfil och lägger till följande i <dict> avsnittet. Ersätt [BUNDLE_ID] med det värde som du använde tidigare. Om du laddade ned koden är com.microsoft.identitysample.MSALiOSpaketidentifieraren . Om du skapar ett eget projekt väljer du projektet i Xcode och öppnar fliken Allmänt . Paketidentifieraren visas i avsnittet Identitet .

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

Endast för macOS konfigurerar du appens sandbox-miljö

  1. Gå till Xcode Project Inställningar-fliken >Funktioner fliken>App sandbox
  2. Markera kryssrutan Utgående Anslut ions (klient).

Skapa appens användargränssnitt

Skapa nu ett användargränssnitt som innehåller en knapp för att anropa Microsoft Graph API, en annan för att logga ut och en textvy för att se vissa utdata genom att lägga till följande kod i ViewController klassen:

iOS-användargränssnitt

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-användargränssnitt


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

I klassen ersätter viewDidLoad() du sedan ViewController metoden med:

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

Använda MSAL

Initiera MSAL

Lägg till metoden i ViewControllerinitMSAL klassen:

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

Lägg till metoden i ViewController klassen och efter initMSAL - initWebViewParams metoden:

iOS-kod:

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

macOS-kod:

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

Hantera återanropet för inloggning (endast iOS)

Öppna filen AppDelegate.swift. Om du vill hantera återanropet efter inloggningen lägger du till MSALPublicClientApplication.handleMSALResponse i appDelegate klassen så här:

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

Om du använder Xcode 11 bör du placera MSAL-återanrop till SceneDelegate.swift i stället. Om du stöder både UISceneDelegate och UIApplicationDelegate för kompatibilitet med äldre iOS måste MSAL-motringning placeras i båda filerna.

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

Hämta token

Nu kan vi implementera programmets UI-bearbetningslogik och hämta token interaktivt via MSAL.

MSAL exponerar två primära metoder för att hämta token: acquireTokenSilently() och acquireTokenInteractively().

  • acquireTokenSilently() försöker logga in en användare och hämta token utan användarinteraktion så länge ett konto finns. acquireTokenSilently() kräver ett giltigt MSALAccount, som kan hämtas med någon av MSAL:s api:er för kontouppräkning. I den här självstudien används applicationContext.getCurrentAccount(with: msalParameters, completionBlock: {}) för att hämta det aktuella kontot.

  • acquireTokenInteractively() visar alltid användargränssnittet när du försöker logga in användaren. Den kan använda sessionscookies i webbläsaren eller ett konto i Microsoft-autentiseringen för att tillhandahålla en interaktiv SSO-upplevelse.

Lägg till följande kod i ViewController klassen:

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

Hämta en token interaktivt

Följande kodfragment hämtar en token för första gången genom att skapa ett MSALInteractiveTokenParameters objekt och anropa acquireToken. Därefter lägger du till kod som:

  1. Skapar MSALInteractiveTokenParameters med omfång.
  2. Anrop acquireToken() med de skapade parametrarna.
  3. Hanterar fel. Mer information finns i felhanteringsguiden för MSAL för iOS och macOS.
  4. Hanterar det lyckade fallet.

Lägg till följande kod i klassen 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()
    }
}

Egenskapen promptTypeMSALInteractiveTokenParameters för konfigurerar beteendet för autentisering och medgivandefråga. Följande värden stöds:

  • .promptIfNecessary (standard) – Användaren uppmanas endast om det behövs. SSO-upplevelsen bestäms av förekomsten av cookies i webbvyn och kontotypen. Om flera användare är inloggade visas upplevelsen för val av konto. Det här är standardbeteendet.
  • .selectAccount – Om ingen användare har angetts visar webbvyn för autentisering en lista över konton som användaren kan välja mellan för närvarande inloggade konton.
  • .login – Kräver att användaren autentiserar i webbvyn. Endast ett konto kan loggas in i taget om du anger det här värdet.
  • .consent – Kräver att användaren godkänner den aktuella uppsättningen omfång för begäran.

Hämta en token tyst

Om du vill hämta en uppdaterad token tyst lägger du till följande kod i ViewController klassen. Det skapar ett MSALSilentTokenParameters objekt och anropar 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()
        }
    }

Anropa Microsoft Graph API

När du har en token kan appen använda den i HTTP-huvudet för att göra en auktoriserad begäran till Microsoft Graph:

rubriknyckel värde
Auktorisering <Ägaråtkomsttoken>

Lägg till följande kod i ViewController klassen:

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

Mer information om Microsoft Graph API finns i Microsoft Graph API .

Använda MSAL för utloggning

Lägg sedan till stöd för utloggning.

Viktigt!

När du loggar ut med MSAL tas all känd information om en användare bort från programmet, samt en aktiv session på enheten tas bort när den tillåts av enhetskonfigurationen. Du kan också logga ut användaren från webbläsaren.

Lägg till följande kod i ViewController klassen för att lägga till utloggningsfunktionen.

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

        }
    }

Aktivera cachelagring av token

Som standard cachelagrar MSAL appens token i iOS- eller macOS-nyckelringen.

Så här aktiverar du cachelagring av token:

  1. Kontrollera att programmet är korrekt signerat
  2. Gå till fliken>Xcode Project Inställningar >Capabilities (Aktivera nyckelringsdelning)
  3. Välj + och ange någon av följande nyckelringsgrupper:
    • Ios: com.microsoft.adalcache
    • Macos: com.microsoft.identity.universalstorage

Lägga till hjälpmetoder

Lägg till följande hjälpmetoder i ViewController klassen för att slutföra exemplet.

iOS-användargränssnitt:


    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-användargränssnitt:

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

Endast iOS: hämta ytterligare enhetsinformation

Använd följande kod för att läsa den aktuella enhetskonfigurationen, inklusive om enheten är konfigurerad som delad:

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

Program med flera konton

Den här appen är skapad för ett enskilt kontoscenario. MSAL stöder även scenarier med flera konton, men det krävs mer programarbete. Du måste skapa användargränssnitt för att hjälpa användare att välja vilket konto de vill använda för varje åtgärd som kräver token. Du kan också implementera en heuristisk app för att välja vilket konto som ska användas genom att fråga alla konton från MSAL. Se till exempel accountsFromDeviceForParameters:completionBlock:API

Testa din app

Skapa och distribuera appen till en testenhet eller simulator. Du bör kunna logga in och hämta token för Microsoft Entra-ID eller personliga Microsoft-konton.

Första gången en användare loggar in på din app uppmanas de av Microsoft-identiteten att samtycka till de begärda behörigheterna. Även om de flesta användare kan samtycka har vissa Microsoft Entra-klienter inaktiverat användarmedgivande, vilket kräver att administratörer samtycker för alla användares räkning. För att stödja det här scenariot registrerar du appens omfång.

När du har loggat in visar appen data som returneras från Microsoft Graph-slutpunkten /me .

Nästa steg

Läs mer om att skapa mobilappar som anropar skyddade webb-API:er i vår scenarioserie i flera delar.