共用方式為


教學課程:使用原生驗證在 iOS/macOS 應用程式中新增登入與登出

適用於內含白色核取記號的綠色圓圈。 iOS (Swift) 具有白色核取符號的綠色圓圈。 macOS (Swift)

本教學課程示範如何使用原生驗證,在 iOS/macOS 應用程式中利用電子郵件一次性密碼或使用者名稱及密碼登入與登出使用者。

在本教學課程中,您會了解如何:

  • 使用電子郵件一次性密碼或使用者名稱 (電子郵件) 及密碼登入使用者。
  • 登出使用者。
  • 處理登入錯誤

必要條件

登入使用者

若要使用 [電子郵件一次性密碼] 流程登入使用者,請擷取電子郵件,並傳送包含一次性密碼的電子郵件,讓使用者驗證其電子郵件。 使用者輸入有效的一次性密碼後,應用程式便會將其登入。

若要使用 電子郵件搭配密碼 流程登入使用者,請擷取電子郵件和密碼。 若使用者名稱及密碼有效,應用程式便會登入使用者。

若要登入使用者,您必須:

  1. 建立使用者介面 (UI) 以:

    • 收集使用者的電子郵件。 在輸入新增驗證,確保使用者輸入有效的電子郵件地址。
    • 若要以使用者名稱 (電子郵件) 及密碼登入,請收集密碼。
    • 若要以電子郵件一次性密碼登入,請收集使用者的電子郵件一次性密碼。
    • 新增一個按鈕,讓使用者在使用電子郵件一次性密碼登入時重新發送一次性密碼。
  2. 在 UI 中新增其選取事件會觸發登入的按鈕,如下列程式碼片段所示:

        @IBAction func signInPressed(_: Any) {
        guard let email = emailTextField.text else {
            resultTextView.text = "email not set"
            return
        }
    
        let parameters = MSALNativeAuthSignInParameters(username: email)
        nativeAuth.signIn(parameters: parameters, delegate: self)
    }
    

    若要使用 電子郵件一次性密碼 流程登入使用者,我們會使用下列程式碼片段:

    nativeAuth.signIn(parameters: parameters, delegate: self)
    

    signIn(parameters:delegate) 方法,藉由對傳遞的委派物件呼叫其中一個方法 (必須實作 SignInStartDelegate 通訊協定),以非同步方式回應。 我們會傳遞一個包含使用者在電子郵件提交表單中所提供電子郵件位址的 MSALNativeAuthSignInParameters 例項,並將 self 作為代理傳遞。

    若要使用 電子郵件搭配密碼 流程登入使用者,我們會使用下列程式碼片段:

    let parameters = MSALNativeAuthSignInParameters(username: email)
    parameters.password = password
    nativeAuth.signIn(parameters: parameters, delegate: self)
    

    signIn(parameters:delegate) 方法中,您會傳遞 MSALNativeAuthSignInParameters 實例,其中包含使用者提供的電子郵件位址及其密碼,以及符合 SignInStartDelegate 通訊協定的委派物件。 在此範例,我們會傳遞 self

  3. 若要在使用 SignInStartDelegate 流程時實作 通訊協定,請使用下列程式碼片段:

    extension ViewController: SignInStartDelegate {
        func onSignInStartError(error: MSAL.SignInStartError) {
            resultTextView.text = "Error signing in: \(error.errorDescription ?? "no description")"
        }
    
        func onSignInCodeRequired(
            newState: MSAL.SignInCodeRequiredState,
            sentTo: String,
            channelTargetType: MSAL.MSALNativeAuthChannelType,
            codeLength: Int
        ) {
            resultTextView.text = "Verification code sent to \(sentTo)"
        }
    }
    

    signIn(parameters:delegate) 會導致呼叫委派方法。 在最常見的案例中,會呼叫 onSignInCodeRequired(newState:sentTo:channelTargetType:codeLength) 來表示已傳送代碼以驗證使用者的電子郵件地址。 除了一些已傳送程式碼位置的詳細資料,以及程式碼包含的位數之外,此委派方法還有型別 newStateSignInCodeRequiredState 參數,這可讓我們存取以下兩個新方法:

    • submitCode(code:delegate)
    • resendCode(delegate)

    若要使用 submitCode(code:delegate) 提交使用者以一次性密碼形式提供的一次性密碼,請使用下列程式碼片段:

    newState.submitCode(code: userSuppliedCode, delegate: self)
    

    submitCode(code:delegate) 接受一次性密碼和委派參數。 提交代碼之後,您必須實作 SignInVerifyCodeDelegate 通訊協定來驗證一次性密碼。

    若要將 SignInVerifyCodeDelegate 通訊協定實作為類別的延伸模組,請使用下列程式碼片段:

    extension ViewController: SignInVerifyCodeDelegate {
        func onSignInVerifyCodeError(error: MSAL.VerifyCodeError, newState: MSAL.SignInCodeRequiredState?) {
            resultTextView.text = "Error verifying code: \(error.errorDescription ?? "no description")"
        }
    
        func onSignInCompleted(result: MSALNativeAuthUserAccountResult) {
            resultTextView.text = "Signed in successfully."
            let parameters = MSALNativeAuthGetAccessTokenParameters()
            result.getAccessToken(parameters: parameters, delegate: self)
        }
    }
    

    在最常見的案例中,我們會收到 onSignInCompleted(result) 呼叫,指出使用者已登入。 此結果可用來擷取 access token

    getAccessToken(parameters:delegate) 接受 MSALNativeAuthGetAccessTokenParameters 實例和委派參數,我們必須在 CredentialsDelegate 通訊協定中實作必要的方法。

    在最常見的案例中,我們會收到 onAccessTokenRetrieveCompleted(result) 的呼叫,表示使用者已取得 access token

    extension ViewController: CredentialsDelegate {
        func onAccessTokenRetrieveError(error: MSAL.RetrieveAccessTokenError) {
            resultTextView.text = "Error retrieving access token"
        }
    
        func onAccessTokenRetrieveCompleted(result: MSALNativeAuthTokenResult) {
            resultTextView.text = "Signed in. Access Token: \(result.accessToken)"
        }
    }
    
    
  4. 若要使用 SignInStartDelegate 流程時實作 通訊協定,請使用下列程式碼片段:

    extension ViewController: SignInStartDelegate {
        func onSignInStartError(error: MSAL.SignInStartError) {
            resultTextView.text = "Error signing in: \(error.errorDescription ?? "no description")"
        }
    
        func onSignInCompleted(result: MSAL.MSALNativeAuthUserAccountResult) {
            // User successfully signed in
        }
    }
    

    在最常見的案例中,我們會收到 onSignInCompleted(result) 呼叫,指出使用者已登入。 此結果可用來擷取 access token

    getAccessToken(parameters:delegate) 接受 MSALNativeAuthGetAccessTokenParameters 實例和委派參數,我們必須在 CredentialsDelegate 通訊協定中實作必要的方法。

    在最常見的案例中,我們會收到 onAccessTokenRetrieveCompleted(result) 的呼叫,表示使用者已取得 access token

    extension ViewController: CredentialsDelegate {
        func onAccessTokenRetrieveError(error: MSAL.RetrieveAccessTokenError) {
            resultTextView.text = "Error retrieving access token"
        }
    
        func onAccessTokenRetrieveCompleted(result: MSALNativeAuthTokenResult) {
            resultTextView.text = "Signed in. Access Token: \(result.accessToken)"
        }
    }
    
    

處理登入錯誤

登入期間,並非每個動作都成功。 例如,使用者可能會嘗試使用不存在的電子郵件地址來登入,或提交無效的代碼。

  1. 若要處理 signIn(parameters:delegate) 方法中的錯誤,請使用下列代碼段:

    func onSignInStartError(error: MSAL.SignInStartError) {
        if error.isUserNotFound || error.isInvalidUsername {
            resultTextView.text = "Invalid username"
        } else {
            resultTextView.text = "Error signing in: \(error.errorDescription ?? "no description")"
        }
    }
    
  2. 若要處理 submitCode() 方法中的錯誤,請使用下列程式碼片段:

    func onSignInVerifyCodeError(error: MSAL.VerifyCodeError, newState: MSAL.SignInCodeRequiredState?) {
        if error.isInvalidCode {
            // Inform the user that the submitted code was incorrect and ask for a new code to be supplied
            let userSuppliedCode = retrieveNewCode()
            newState?.submitCode(code: userSuppliedCode, delegate: self)
        } else {
            resultTextView.text = "Error verifying code: \(error.errorDescription ?? "no description")"
        }
    }
    

    如果使用者輸入不正確的電子郵件驗證碼,則錯誤處理常式會包含有可用來提交更新代碼的 SignInCodeRequiredState 參考。 在先前的 SignInVerifyCodeDelegate 通訊協定實作中,我們只會在處理 onSignInVerifyCodeError(error:newState) 委派函式時顯示錯誤。

讀取識別碼權杖宣告

應用程式取得識別碼權杖後,您就可以擷取與目前帳戶相關聯的宣告。 若要這樣做,請使用下列程式碼片段:

func onSignInCompleted(result: MSAL.MSALNativeAuthUserAccountResult) {
   let claims = result.account.accountClaims
   let preferredUsername = claims?["preferred_username"] as? String
}

用於存取宣告值的索引鍵,即為您將使用者屬性新增為權杖宣告時所指定的名稱。

若要了解如何將內建和自訂屬性新增為權杖宣告,請參閱新增使用者屬性至權杖宣告一文。

登出使用者

若要登出使用者,請使用您在 MSALNativeAuthUserAccountResult 回撥中收到的 onSignInCompleted 參考,或使用 getNativeAuthUserAccount() 從快取取得任何登入的帳戶,並將參考儲存在 accountResult 成員變數中。

  1. 這裡所述,為您的專案設定金鑰鏈群組。

  2. 將新的成員變數新增至您的 ViewController 類別:var accountResult: MSALNativeAuthUserAccountResult?

  3. 成功初始化 viewDidLoad 之後新增這一行,以更新 nativeAuth 來擷取任何快取的帳戶:accountResult = nativeAuth.getNativeAuthUserAccount()

  4. 更新 signInCompleted 處理常式以儲存帳戶結果:

    func onSignInCompleted(result: MSALNativeAuthUserAccountResult) {
        resultTextView.text = "Signed in successfully"
    
        accountResult = result
    }
    
  5. 新增 [登出] 按鈕,並使用下列代碼來登出使用者:

    @IBAction func signOutPressed(_: Any) {
        guard let accountResult = accountResult else {
            print("Not currently signed in")
            return
        }
    
        accountResult.signOut()
    
        self.accountResult = nil
    
        resultTextView.text = "Signed out"
    }
    

您已成功完成在應用程式上登出使用者所需的所有步驟。 建置並執行您的應用程式。 若一切順利,您應能選取 [登出] 按鈕以成功登出。

設定自訂宣告提供者

如果您想要將來自外部系統的宣告新增至發行至應用程式的權杖,請使用 自訂宣告提供者。 自訂宣告提供者是由自訂驗證擴充功能組成,可呼叫外部 REST API 從外部系統擷取宣告。

請遵循 設定自訂宣告提供者 中的步驟,將外部系統的宣告新增至您的安全性權杖。