Öğretici: iOS veya macOS uygulamasından kullanıcılarda oturum açma ve Microsoft Graph'ı çağırma

Bu öğreticide, kullanıcıları imzalamak ve Microsoft Graph API'sini çağırmak için erişim belirteci almak için Microsoft kimlik platformu ile tümleşen bir iOS veya macOS uygulaması oluşturacaksınız.

Öğreticiyi tamamladığınızda, uygulamanız Microsoft Entra Id kullanan herhangi bir şirket veya kuruluştan kişisel Microsoft hesaplarının (outlook.com, live.com ve diğerleri dahil) ve iş veya okul hesaplarının oturum açmalarını kabul eder. Bu öğretici hem iOS hem de macOS uygulamaları için geçerlidir. Bazı adımlar iki platform arasında farklıdır.

Bu öğreticide:

  • Xcode'da iOS veya macOS uygulama projesi oluşturma
  • Uygulamayı Microsoft Entra yönetim merkezine kaydetme
  • Kullanıcı oturum açma ve oturum kapatmayı desteklemek için kod ekleme
  • Microsoft Graph API'sini çağırmak için kod ekleme
  • Uygulamayı test etme

Önkoşullar

Öğretici uygulaması nasıl çalışır?

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

Bu öğreticideki uygulama, kullanıcılarda oturum açabilir ve kendi adlarına Microsoft Graph'ten veri alabilir. Bu verilere yetkilendirme gerektiren ve Microsoft kimlik platformu tarafından korunan bir korumalı API (bu örnekte Microsoft Graph API'si) aracılığıyla erişilir.

Daha açık belirtmek gerekirse:

  • Uygulamanız bir tarayıcı veya Microsoft Authenticator aracılığıyla kullanıcıda oturum açar.
  • Son kullanıcı, uygulamanızın istediği izinleri kabul eder.
  • Uygulamanıza Microsoft Graph API için bir erişim belirteci verilir.
  • Erişim belirteci, web API'sine http isteğine eklenir.
  • Microsoft Graph yanıtını işleme.

Bu örnek, Kimlik Doğrulamasını uygulamak için Microsoft Kimlik Doğrulama Kitaplığı'nı (MSAL) kullanır. MSAL belirteçleri otomatik olarak yeniler, cihazdaki diğer uygulamalar arasında çoklu oturum açma (SSO) sunar ve hesapları yönetir.

Bu öğreticide oluşturduğunuz uygulamanın tamamlanmış bir sürümünü indirmek isterseniz gitHub'da her iki sürümü de bulabilirsiniz:

Yeni proje oluşturma

  1. Xcode'u açın ve Yeni Xcode projesi oluştur'u seçin.
  2. iOS uygulamaları için iOS>Tek görünüm Uygulaması'nı ve ardından İleri'yi seçin.
  3. macOS uygulamaları için macOS>Cocoa Uygulaması'nı ve ardından İleri'yi seçin.
  4. Bir ürün adı belirtin.
  5. Dil'i Swift olarak ayarlayın ve İleri'yi seçin.
  6. Uygulamanızı oluşturmak için bir klasör seçin ve Oluştur'u seçin.

Uygulamayı kaydetme

Bahşiş

Bu makaledeki adımlar, başladığınız portala göre biraz değişiklik gösterebilir.

  1. Microsoft Entra yönetim merkezinde en azından Uygulama Geliştiricisi olarak oturum açın.
  2. Birden çok kiracıya erişiminiz varsa, dizinler + abonelikler menüsünden uygulamayı kaydetmek istediğiniz kiracıya geçmek için üst menüdeki Ayarlar simgesini kullanın.
  3. Kimlik>Uygulamaları'na> göz atın Uygulama kayıtları.
  4. Yeni kayıt öğesini seçin.
  5. Uygulamanız için bir Ad girin. Uygulamanızın kullanıcıları bu adı görebilir ve daha sonra değiştirebilirsiniz.
  6. Desteklenen hesap türleri altından herhangi bir kuruluş dizininde (Herhangi bir Microsoft Entra dizini - Çok Kiracılı) ve kişisel Microsoft hesaplarında (örneğin Skype, Xbox) Hesaplar'ı seçin.
  7. Kaydet'i seçin.
  8. Yönet'in altında Kimlik Doğrulaması>Platform>ekle iOS/macOS'ı seçin.
  9. Projenizin Paket Kimliğini girin. Kod örneği indirilirse Paket Kimliği olur com.microsoft.identitysample.MSALiOS. Kendi projenizi oluşturuyorsanız, Xcode'da projenizi seçin ve Genel sekmesini açın. Paket tanımlayıcısı Kimlik bölümünde görünür.
  10. Yapılandır'ı seçin ve uygulamanızı daha sonra yapılandırırken girebilmeniz için MSAL yapılandırma sayfasında görüntülenen MSAL Yapılandırmasını kaydedin.
  11. Bitti'yi seçin.

MSAL Ekleme

MSAL kitaplığını uygulamanıza yüklemek için aşağıdaki yollardan birini seçin:

CocoaPods

  1. CocoaPods kullanıyorsanız, önce projenizin .xcodeproj dosyasıyla aynı klasörde podfile adlı boş bir dosya oluşturarak yükleyinMSAL. Podfile'a aşağıdakileri ekleyin:

    use_frameworks!
    
    target '<your-target-here>' do
       pod 'MSAL'
    end
    
  2. değerini projenizin adıyla değiştirin <your-target-here> .

  3. Terminal penceresinde, oluşturduğunuz pod dosyasını içeren klasöre gidin ve MSAL kitaplığını yüklemek için çalıştırınpod install.

  4. Xcode'ı kapatın ve projeyi Xcode'da yeniden yüklemek için açın <your project name>.xcworkspace .

Kartaca

Carthage kullanıyorsanız Cartfile dosyanıza ekleyerek yükleyinMSAL:

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

Bir terminal penceresinde, güncelleştirilmiş Cartfile ile aynı dizinde, Carthage'ın projenizdeki bağımlılıkları güncelleştirmesini sağlamak için aşağıdaki komutu çalıştırın.

Ios:

carthage update --platform iOS

macOS:

carthage update --platform macOS

El ile

Ayrıca Git Submodule'i kullanabilir veya uygulamanızda çerçeve olarak kullanmak için en son sürüme göz atabilirsiniz.

Uygulama kaydını ekleme

Ardından uygulama kaydınızı kodunuza ekleyeceğiz.

İlk olarak, aşağıdaki içeri aktarma deyimini ViewController.swift dosyasının en üstüne ve AppDelegate.swift veya SceneDelegate.swift dosyasına ekleyin:

import MSAL

Ardından, aşağıdaki kodu viewcontroller.swift'e viewDidLoad()ekleyin:

// 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ğiştirdiğiniz tek değer, Uygulama Kimliğiniz olarak atanan kClientID değerdir. Bu değer, uygulamayı kaydetmek için bu öğreticinin başındaki adım sırasında kaydettiğiniz MSAL Yapılandırma verilerinin bir parçasıdır.

Xcode proje ayarlarını yapılandırma

Proje İmzalama ve Yeteneklerinize yeni bir anahtarlık grubu ekleyin. Anahtarlık grubu iOS ve com.microsoft.identity.universalstorage macOS üzerinde olmalıdırcom.microsoft.adalcache.

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

Yalnızca iOS için URL düzenlerini yapılandırın

Bu adımda, kullanıcının oturum açmadan sonra uygulamaya yeniden yönlendirilmesi için kaydolacaksınız CFBundleURLSchemes . Bu arada, LSApplicationQueriesSchemes uygulamanızın Microsoft Authenticator'ı kullanmasına da izin verir.

Xcode'da Info.plist dosyasını kaynak kod dosyası olarak açın ve bölümün <dict> içine aşağıdakileri ekleyin. değerini daha önce kullandığınız değerle değiştirin [BUNDLE_ID] . Kodu indirdiyseniz paket tanımlayıcısı olur com.microsoft.identitysample.MSALiOS. Kendi projenizi oluşturuyorsanız, Xcode'da projenizi seçin ve Genel sekmesini açın. Paket tanımlayıcısı Kimlik bölümünde görünür.

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

Yalnızca macOS için Uygulama Korumalı Alanını yapılandırın

  1. Xcode Proje Ayarlar >Özellikleri sekmenize>gidin Uygulama Korumalı Alanı
  2. Giden Bağlan ions (İstemci) onay kutusunu seçin.

Uygulamanızın kullanıcı arabirimini oluşturma

Şimdi, sınıfına aşağıdaki kodu ViewController ekleyerek Microsoft Graph API'sini çağırmak için bir düğme, oturumu kapatmak için başka bir düğme ve bazı çıktıları görmek için bir metin görünümü içeren bir kullanıcı arabirimi oluşturun:

iOS kullanıcı arabirimi

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 kullanıcı arabirimi


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

Ardından, sınıfının içinde ViewController de yöntemini şununla viewDidLoad() değiştirin:

    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 kullanma

MSAL'ı başlatma

sınıfına ViewController yöntemini ekleyin initMSAL :

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

ViewController Hala sınıfında ve yönteminden initMSAL sonra yöntemini ekleyininitWebViewParams:

iOS kodu:

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

macOS kodu:

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

Oturum açma geri çağırmasını işleme (yalnızca iOS)

AppDelegate.swift dosyasını açın. Oturum açmadan sonra geri çağırmayı işlemek için sınıfına appDelegate şu şekilde ekleyinMSALPublicClientApplication.handleMSALResponse:

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

Xcode 11 kullanıyorsanız bunun yerine MSAL geri çağırmasını SceneDelegate.swift içine yerleştirmeniz gerekir. Eski iOS ile uyumluluk için hem UISceneDelegate hem de UIApplicationDelegate'i destekliyorsanız, MSAL geri çağırmanın her iki dosyaya da yerleştirilmesi gerekir.

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

Belirteçleri Alma

Artık uygulamanın kullanıcı arabirimi işleme mantığını uygulayabilir ve MSAL aracılığıyla belirteçleri etkileşimli olarak alacağız.

MSAL, belirteçleri almak için iki birincil yöntem sunar: acquireTokenSilently() ve acquireTokenInteractively().

  • acquireTokenSilently() bir hesap mevcut olduğu sürece kullanıcı etkileşimi olmadan bir kullanıcıda oturum açmayı ve belirteçleri almayı dener. acquireTokenSilently() MSAL'nin hesap numaralandırma API'lerinden biri kullanılarak alınabilen geçerli MSALAccountbir gerektirir. Bu öğreticide geçerli hesabı almak için kullanılır applicationContext.getCurrentAccount(with: msalParameters, completionBlock: {}) .

  • acquireTokenInteractively() kullanıcı oturum açmaya çalışırken her zaman kullanıcı arabirimini gösterir. Etkileşimli bir SSO deneyimi sağlamak için tarayıcıda oturum tanımlama bilgilerini veya Microsoft kimlik doğrulayıcıdaki bir hesabı kullanabilir.

Sınıfına ViewController aşağıdaki kodu ekleyin:

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

Etkileşimli olarak belirteç alma

Aşağıdaki kod parçacığı, bir nesnesi oluşturup çağırarak acquireTokenilk kez bir MSALInteractiveTokenParameters belirteç alır. Ardından şu kodu eklersiniz:

  1. Kapsamlarla oluşturur MSALInteractiveTokenParameters .
  2. Oluşturulan parametrelerle çağrılar acquireToken() .
  3. Hataları işler. Daha fazla ayrıntı için iOS ve macOS için MSAL hata işleme kılavuzuna bakın.
  4. Başarılı olayı işler.

Aşağıdaki kodu ViewController sınıfına ekleyin.

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

promptType özelliğiMSALInteractiveTokenParameters, kimlik doğrulaması ve onay istemi davranışını yapılandırmaktadır. Aşağıdaki değerler desteklenir:

  • .promptIfNecessary (varsayılan) - Kullanıcıdan yalnızca gerekirse istenir. SSO deneyimi, web görünümünde tanımlama bilgilerinin bulunmasına ve hesap türüne göre belirlenir. Birden çok kullanıcı oturum açtıysa hesap seçimi deneyimi sunulur. Bu, varsayılan davranıştır.
  • .selectAccount - Kullanıcı belirtilmezse, kimlik doğrulama web görünümü kullanıcının seçecekleri oturum açmış hesapların listesini sunar.
  • .login - Kullanıcının web görünümünde kimlik doğrulamasını gerektirir. Bu değeri belirtirseniz aynı anda yalnızca bir hesap oturum açabilir.
  • .consent - Kullanıcının istek için geçerli kapsam kümesini onaylamasını gerektirir.

Belirteci sessizce alma

Güncelleştirilmiş belirteci sessizce almak için sınıfına ViewController aşağıdaki kodu ekleyin. Bir MSALSilentTokenParameters nesnesi oluşturur ve öğesini çağırır 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()
        }
    }

Microsoft Graph API'sini çağırma

Belirteci aldıktan sonra uygulamanız, Microsoft Graph'a yetkili bir istekte bulunmak için bu belirteci HTTP üst bilgisinde kullanabilir:

üst bilgi anahtarı değer
Yetkilendirme Taşıyıcı <erişim belirteci>

Sınıfına ViewController aşağıdaki kodu ekleyin:

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

Microsoft Graph API'si hakkında daha fazla bilgi edinmek için bkz. Microsoft Graph API'si.

Oturumu kapatmak için MSAL kullanma

Ardından, oturumu kapatma desteği ekleyin.

Önemli

MSAL ile oturum kapatıldığında kullanıcı hakkındaki tüm bilinen bilgiler uygulamadan kaldırılır ve cihaz yapılandırmasına izin verildiğinde cihazında etkin bir oturum kaldırılır. İsterseniz kullanıcının tarayıcıdan oturumunu kapatabilirsiniz.

Oturum kapatma özelliği eklemek için sınıfına ViewController aşağıdaki kodu ekleyin.

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

        }
    }

Belirteç önbelleğe almayı etkinleştirme

Varsayılan olarak MSAL, uygulamanızın belirteçlerini iOS veya macOS anahtar zincirinde önbelleğe alır.

Belirteç önbelleğe almayı etkinleştirmek için:

  1. Uygulamanızın düzgün imzalandığından emin olun
  2. Xcode Proje Ayarlar >Özellikleri sekmenize>gidin Anahtarlık Paylaşımını Etkinleştir
  3. Aşağıdaki Anahtar Zinciri Gruplarından birini seçin + ve girin:
    • Ios: com.microsoft.adalcache
    • Macos: com.microsoft.identity.universalstorage

Yardımcı yöntemler ekleme

Örneği tamamlamak için sınıfına ViewController aşağıdaki yardımcı yöntemleri ekleyin.

iOS kullanıcı arabirimi:


    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 kullanıcı arabirimi:

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

Yalnızca iOS: Ek cihaz bilgilerini alma

Cihazın paylaşılan olarak yapılandırılıp yapılandırılmadığı da dahil olmak üzere geçerli cihaz yapılandırmasını okumak için aşağıdaki kodu kullanın:

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

Çok hesaplı uygulamalar

Bu uygulama tek bir hesap senaryosu için oluşturulmuş. MSAL, çok hesaplı senaryoları da destekler, ancak daha fazla uygulama çalışması gerektirir. Kullanıcıların belirteç gerektiren her eylem için kullanmak istedikleri hesabı seçmelerine yardımcı olmak için kullanıcı arabirimi oluşturmanız gerekir. Alternatif olarak, uygulamanız MSAL'den tüm hesapları sorgulayarak hangi hesabın kullanılacağını seçmek için bir buluşsal yöntem uygulayabilir. Örneğin, bkz. accountsFromDeviceForParameters:completionBlock:API

Uygulamanızı test etme

Uygulamayı derleyin ve bir test cihazına veya simülatörüne dağıtın. Oturum açabilmeniz ve Microsoft Entra Id veya kişisel Microsoft hesapları için belirteçler alabilmeniz gerekir.

Bir kullanıcı uygulamanızda ilk kez oturum açtığında, microsoft kimliği tarafından istenen izinleri onaylaması istenir. Kullanıcıların çoğu onay verebilir ancak bazı Microsoft Entra kiracıları, yöneticilerin tüm kullanıcılar adına onay vermelerini gerektiren kullanıcı onayını devre dışı bırakmıştır. Bu senaryoya destek olmak için uygulamanızın kapsamlarını kaydedin.

Oturum açtığınızda uygulama, Microsoft Graph /me uç noktasından döndürülen verileri görüntüler.

Sonraki adımlar

Çok parçalı senaryo serimizde korumalı web API'lerini çağıran mobil uygulamalar oluşturma hakkında daha fazla bilgi edinin.