Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
              Gäller för: 
 Externa klienter (läs mer)
 Externa klienter (läs mer)
Det här är den tredje handledningen i handledningsserien som hjälper dig att logga in användare genom att använda Microsoft Entra ID.
Innan du börjar använder du Välj en klienttyp väljare överst på den här sidan för att välja klienttyp. Microsoft Entra ID tillhandahåller två klientkonfigurationer, personal och externa. En klientkonfiguration för personal är avsedd för dina anställda, interna appar och andra organisationsresurser. En extern hyresgäst är avsedd för dina kundvända appar.
I den här genomgången ska du:
- Logga in användare.
- Logga ut användare.
- Skapa appens användargränssnitt
Förutsättningar
Logga in användare
Du har två huvudsakliga alternativ för att autentisera användare med hjälp av Microsoft Authentication Library (MSAL) för iOS: att hämta tokens interaktivt eller tyst.
- Om du vill logga in användare interaktivt använder du följande kod: - 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 - promptTypeför- MSALInteractiveTokenParameterskonfigurerar beteendet för autentiserings- och medgivandeförfrågningar. 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 processen 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 tillfället 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 behörigheter för denna begäran.
 
- 
              
- Om du vill logga in användaren tyst använder du följande kod: - 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() } }- Metoden - acquireTokenSilentlyförsöker tyst hämta en åtkomsttoken för ett befintligt MSAL-konto. Det här använder- applicationContextför att begära token med de angivna behörigheterna. Om ett fel uppstår kontrollerar den om användarinteraktion krävs och initierar i så fall ett interaktivt tokenförvärv. När det har lyckats uppdaterar den åtkomsttoken, loggar resultatet, aktiverar utloggningsknappen och hämtar innehåll med hjälp av token.
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 klassen appDelegate 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 11bör du placera MSAL-callback i SceneDelegate.swift i stället. Om du stöder både UISceneDelegate och UIApplicationDelegate för kompatibilitet med äldre iOS måste MSAL-callback läggas till 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)
    }
Logga ut användare
Viktig
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 klassen ViewController om du vill 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)
            })
        }
    }
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 klassen ViewController:
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 ViewController ersätter du sedan metoden viewDidLoad() 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()
    }
Nästa steg
Det här är den tredje handledningen i handledningsserien som hjälper dig att logga in användare genom att använda Microsoft Entra ID.
Innan du börjar använder du Välj en klienttyp väljare överst på den här sidan för att välja klienttyp. Microsoft Entra ID tillhandahåller två klientkonfigurationer, personal och externa. En klientkonfiguration för personal är avsedd för dina anställda, interna appar och andra organisationsresurser. En extern hyresgäst är avsedd för dina kundvända appar.
I den här genomgången ska du:
- Logga in användare.
- Logga ut användare.
Förutsättningar
Logga in användare
Du har två huvudsakliga alternativ för att autentisera användare med hjälp av Microsoft Authentication Library (MSAL) för iOS: att hämta tokens interaktivt eller tyst.
- Om du vill logga in användare interaktivt använder du följande kod: - acquireTokenInteractively() { guard let applicationContext = self.applicationContext else { return } guard let webViewParameters = self.webViewParameters else { return } updateLogging(text: "Acquiring token interactively...") let parameters = MSALInteractiveTokenParameters(scopes: Configuration.kScopes, webviewParameters: webViewParameters) parameters.promptType = .selectAccount applicationContext.acquireToken(with: parameters) { (result, error) in 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 } self.accessToken = result.accessToken self.updateLogging(text: "Access token is \(self.accessToken)") self.updateCurrentAccount(account: result.account) } }- Koden kontrollerar först om programkontexten och webbvyparametrarna är tillgängliga. Sedan uppdateras loggningen för att indikera att den hämtar token interaktivt. Därefter konfigureras parametrar för interaktiv tokeninhämtning, genom att specificera omfång och webbvyns parametrar. Den anger också prompttypen för att välja ett konto. - Därefter anropas metoden - acquireTokeni programkontexten med de definierade parametrarna. I slutförandehanteraren söker den efter eventuella fel. Om ett fel påträffas uppdateras loggningen med felmeddelandet. Om det lyckas hämtar den åtkomsttoken från resultatet, uppdaterar loggningen med token och uppdaterar det aktuella kontot.- När appen har hämtat en åtkomsttoken kan du hämta anspråken som är associerade med det aktuella kontot. Om du vill göra det använder du följande kodfragment: - let claims = result.account.accountClaims let preferredUsername = claims?["preferred_username"] as? String- Koden läser anspråk från kontot genom att komma åt egenskapen - accountClaimsi- result.account-objektet. Sedan hämtar den värdet för anspråket "preferred_username" från anspråksordlistan och tilldelar det till variabeln- preferredUsername.
- Om du vill logga in användaren tyst använder du följande kod: - func acquireTokenSilently() { self.loadCurrentAccount { (account) in guard let currentAccount = account else { self.updateLogging(text: "No token found, try to acquire a token interactively first") return } self.acquireTokenSilently(currentAccount) } }- Koden initierar processen för att tyst hämta tokens. Först försöker programmet läsa in det aktuella kontot. Om ett aktuellt användarkonto hittas, fortsätter det att tyst hämta token med det kontot. Om inget aktuellt konto hittas uppdateras loggningen för att indikera att ingen token hittas och föreslår att du försöker hämta en token interaktivt först. - I koden ovan anropar vi två funktioner, - loadCurrentAccountoch- acquireTokenSilently. Funktionen- loadCurrentAccountska ha följande kod:- func loadCurrentAccount(completion: AccountCompletion? = nil) { guard let applicationContext = self.applicationContext else { return } let msalParameters = MSALParameters() msalParameters.completionBlockQueue = DispatchQueue.main // Note that this sample showcases an app that signs in a single account at a time 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.updateCurrentAccount(account: currentAccount) self.acquireTokenSilently(currentAccount) if let completion = completion { completion(self.currentAccount) } return } // If testing with Microsoft's shared device mode, see the account that has been signed out from another app. More details here: // https://docs.microsoft.com/azure/active-directory/develop/msal-ios-shared-devices if let previousAccount = previousAccount { self.updateLogging(text: "The account with username \(String(describing: previousAccount.username)) has been signed out.") } else { self.updateLogging(text: "") } self.accessToken = "" self.updateCurrentAccount(account: nil) if let completion = completion { completion(nil) } }) }- Koden använder MSAL för iOS för att ladda in det aktuella kontot. Den söker efter fel och uppdaterar loggningen i enlighet med detta. Om ett aktuellt konto hittas uppdateras det och försöker i tysthet få tag i tokens. Om det finns ett tidigare konto loggar det utloggningen. Om inga konton hittas rensas åtkomsttoken. Slutligen körs ett slutförandeblock om det finns tillgängligt. - Funktionen - acquireTokenSilentlyska innehålla följande kod:- 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. */ updateLogging(text: "Acquiring token silently...") let parameters = MSALSilentTokenParameters(scopes: Configuration.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) } }- Den här funktionen använder MSAL för iOS för att tyst hämta en token för ett befintligt konto. När du har verifierat - applicationContextloggar den processen för tokenanskaffning. Med hjälp av- MSALSilentTokenParametersdefinierar den nödvändiga parametrar. Sedan försöker den hämta token diskret. Om det finns fel söker den efter användarinteraktionskrav och initierar en interaktiv process om det behövs. När det lyckas uppdaterar den- accessToken-egenskapen och loggar den förnyade token, och avslutar med att aktivera utloggningsknappen.
Logga ut användare
Om du vill logga ut en användare från din iOS-app (Swift) med MSAL för iOS använder du följande kod:
   @IBAction func signOut(_ sender: UIButton) {
        guard let applicationContext = self.applicationContext else { return }
        guard let account = self.currentAccount else { return }
        guard let webViewParameters = self.webViewParameters else { return }
        updateLogging(text: "Signing out...")
        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: webViewParameters)
            // If testing with Microsoft's shared device mode, trigger signout from browser. More details here:
            // https://docs.microsoft.com/azure/active-directory/develop/msal-ios-shared-devices
            if (self.currentDeviceMode == .shared) {
                signoutParameters.signoutFromBrowser = true
            } else {
                signoutParameters.signoutFromBrowser = false
            }
            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)
            })
        }
    }
Koden verifierar förekomsten av applicationContext, currentAccountoch webViewParameters. Sedan loggar den utloggningsprocessen. Koden tar bort alla token från cacheminnet för det angivna kontot. Beroende på det aktuella enhetsläget avgör det om du vill logga ut från webbläsaren. När uppgiften är slutförd uppdateras loggtexten därefter. Om ett fel inträffar under utloggningsprocessen loggar det felmeddelandet. När du har loggat ut uppdateras åtkomsttoken till en tom sträng och det aktuella kontot rensas.