チュートリアル: iOS (Swift) モバイル アプリでユーザーをサインインさせる

これは、Microsoft Entra 外部 ID を使用してユーザーをサインインさせる方法を説明するチュートリアル シリーズの 3 番目のチュートリアルです。

このチュートリアルでは、次のことについて説明します。

  • ユーザーをサインインさせる。
  • ユーザーをサインアウトさせる。

前提条件

ユーザーのサインイン

iOS 用 Microsoft 認証ライブラリ (MSAL) を使用してユーザーをサインインさせるための主なオプションとしては、対話形式またはサイレント モードでのトークンの取得の 2 つがあります。

  1. 対話形式でユーザーをサインインさせるには、次のコードを使用します。

    acquireTokenInteractively() {
        guard let applicationContext = self.applicationContext else { return }
        guard let webViewParameters = self.webViewParamaters 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)
        }
    }
    

    このコードはまず、アプリケーション コンテキストと Web ビューのパラメーターが使用可能かどうかを確認します。 その後、ログを更新して、トークンを対話形式で取得していることを示します。 次に、対話型トークン取得用のパラメーターを設定し、スコープと Web ビューのパラメーターを指定します。 また、アカウントを選択するためのプロンプトの種類も設定します。

    その後、定義されたパラメーターを使用して、アプリケーション コンテキストで acquireToken メソッドを呼び出します。 完了ハンドラーでは、エラーがチェックされます。 エラーが発生した場合は、エラー メッセージでログを更新します。 成功した場合は、結果からアクセス トークンを取得し、トークンを使用してログを更新し、現在のアカウントを更新します。

    アプリがアクセス トークンを獲得したら、現在のアカウントに関連付けられているクレームを取得できます。 これを行うには、次のコード スニペットを使用します。

    let claims = result.account.accountClaims
    let preferredUsername = claims?["preferred_username"] as? String
    

    このコードは、result.account オブジェクトの accountClaims プロパティにアクセスして、アカウントからクレームを読み取ります。 次に、クレーム ディクショナリから "preferred_username" クレームの値を取得し、それを preferredUsername 変数に割り当てます。

  2. サイレント モードでユーザーをサインインさせるには、次のコードを使用します。

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

    このコードは、サイレント モードでトークンを取得するプロセスを開始します。 最初に、現在のアカウントの読み込みを試みます。 現在のアカウントが見つかった場合は、そのアカウントを使用してサイレント モードでトークンの取得を続行します。 現在のアカウントが見つからなかった場合は、ログを更新してトークンが見つからないことを示し、最初に対話形式でトークンの取得を試行することを提案します。

    上記のコードでは、loadCurrentAccountacquireTokenSilently の 2 つの関数を呼び出しています。 loadCurrentAccount 関数には次のコードが必要です。

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

    このコードは、iOS 用 MSAL を使用して現在のアカウントを読み込みます。 エラーをチェックし、それに応じてログを更新します。 現在のアカウントが見つかった場合は、それを更新し、サイレント モードでトークンの取得を試みます。 以前のアカウントが存在する場合は、サインアウトをログに記録します。アカウントが見つからなかった場合は、アクセス トークンをクリアします。 最後に、指定されている場合は完了ブロックを実行します。

    acquireTokenSilently 関数には次のコードが含まれている必要があります。

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

    この関数は、iOS 用 MSAL を使用して、既存のアカウントのトークンをサイレント モードで取得します。 applicationContext を検証した後、トークンの取得プロセスをログに記録します。 MSALSilentTokenParameters を使用して、必要なパラメーターを定義します。 次に、サイレント モードでトークンの取得を試みます。 エラーが発生した場合は、ユーザーによる対話型操作の要件をチェックし、必要に応じて対話型プロセスを開始します。 成功した場合は、accessToken プロパティを更新し、更新されたトークンをログに記録し、サインアウト ボタンを有効にして終了します。

ユーザーをサインアウトする

iOS 用 MSAL を使用して iOS (Swift) アプリからユーザーをサインアウトさせるには、次のコードを使用します。

   @IBAction func signOut(_ sender: UIButton) {
        
        guard let applicationContext = self.applicationContext else { return }
        
        guard let account = self.currentAccount else { return }
        
        guard let webViewParamaters = self.webViewParamaters 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: webViewParamaters)
            
            // If testing with Microsoft's shared device mode, trigger signout from browser. More details here:
            // https://docs.microsoft.com/en-us/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)
            })
            
        }
    }

このコードは、applicationContextcurrentAccount、および webViewParamaters の存在を検証します。 次に、サインアウト プロセスをログに記録します。 このコードは、指定されたアカウントのキャッシュからすべてのトークンを削除します。 現在のデバイス モードに応じて、ブラウザーからサインアウトするかどうかを決定します。 完了すると、それに応じてログ テキストを更新します。 サインアウト プロセス中にエラーが発生した場合は、エラー メッセージをログに記録します。 サインアウトに成功すると、アクセス トークンを空の文字列に更新し、現在のアカウントをクリアします。

次のステップ

チュートリアル: iOS (Swift) アプリで保護された Web API を呼び出す