教學:在 iOS/macOS 應用程式中使用原生認證加入註冊功能

適用於帶有白色核取記號符號的綠色圓圈,表示下列內容適用於外部租用戶。 外部租用戶 (深入瞭解

本教學示範如何透過 iOS/macOS 應用程式的電子郵件、一次性密碼或使用者名稱(電子郵件)和密碼,透過原生認證註冊使用者。 你也會學會在註冊時收集使用者屬性,包括使用者名稱(別名),以及處理錯誤。

在本教學課程中,您會:

  • 使用電子郵件一次性密碼或使用者名稱(電子郵件)和密碼註冊使用者。
  • 註冊時收集使用者屬性,包括使用者名稱(別名)。
  • 處理註冊錯誤。

先決條件

註冊使用者

要使用電子郵件一次性密碼或使用者名稱(電子郵件)和密碼註冊用戶,你先從用戶那裡收集電子郵件,然後發送包含一次性密碼的電子郵件給用戶。 使用者輸入有效的電子郵件一次性密碼來驗證其用戶名稱。

若要註冊使用者,您需要:

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

    • 從使用者收集電子郵件。 將驗證新增至您的輸入,以確保使用者輸入有效的電子郵件位址。
    • 如果您使用使用者名稱(電子郵件)和密碼註冊,請收集密碼。
    • 如果你的應用程式支援別名登入,請收集一個用戶名(alias)。
    • 從使用者收集電子郵件一次性密碼。
    • 如有需要,請收集用戶屬性。
    • 如果使用者未收到密碼,請重新傳送一次性密碼。
    • 啟動註冊流程。
  2. 在您的應用程式中,新增按鈕,其 select 事件會觸發下列代碼段:

    @IBAction func signUpPressed(_: Any) {
        guard let email = emailTextField.text else {
            resultTextView.text = "Email or password not set"
            return
        }
    
        let parameters = MSALNativeAuthSignUpParameters(username: email)
        nativeAuth.signUp(parameters: parameters, delegate: self)
    }
    
    • 要使用 Email 一次性密碼註冊使用者,請使用函式庫的方法 signUp(parameters:delegate) ,該方法會非同步呼叫已通過的代理物件中的某個方法,該方法必須實作該 SignUpStartDelegate 協定。 下列程式代碼行會啟動使用者註冊流程:

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

      signUp(parameters:delegate) 方法中,傳遞一個 MSALNativeAuthSignUpParameters 執行個體,其中包含來自送出表單的使用者電子郵件地址,並同時傳入代理物件(即實作 SignUpStartDelegate 協定的類別)。

    • 若要使用 Email 搭配密碼註冊使用者,請使用下列代碼段:

      @IBAction func signUpPressed(_: Any) {
          guard let email = emailTextField.text, let password = passwordTextField.text else {
             resultTextView.text = "Email or password not set"
             return
          }
      
          let parameters = MSALNativeAuthSignUpParameters(username: email)
          parameters.password = password
          nativeAuth.signUp(parameters: parameters, delegate: self)
      }
      

      函式庫的 signUp(parameters:delegate) 方法會透過呼叫傳入的委派物件上的其中一個方法,以非同步方式回應,而該委派物件必須實作 SignUpStartDelegate 協定。 下列程式代碼行會啟動使用者註冊流程:

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

      signUp(parameters:delegate) 方法中,傳遞包含使用者電子郵件地址及密碼的 MSALNativeAuthSignUpParameters 執行個體,以及委派物件(實作 SignUpStartDelegate 協定的類別)。

    • 若要將協定作為 SignUpStartDelegate 類別的擴充,請使用:

      extension ViewController: SignUpStartDelegate {
          func onSignUpStartError(error: MSAL.SignUpStartError) {
              resultTextView.text = "Error signing up: \(error.errorDescription ?? "no description")"
          }
      
          func onSignUpCodeRequired(
              newState: MSAL.SignUpCodeRequiredState,
              sentTo: String,
              channelTargetType: MSAL.MSALNativeAuthChannelType,
              codeLength: Int
          ) {
              resultTextView.text = "Verification code sent to \(sentTo)"
          }
      }
      

      呼叫 signUp(parameters:delegate) 將會觸發 onSignUpCodeRequired()onSignUpStartError() 的委派方法。 使用 onSignUpCodeRequired(newState:sentTo:channelTargetType:codeLength) 來表示已發送驗證碼以確認使用者的電子郵件信箱。 除了程式碼被傳送到哪裡、包含多少位數的細節外,這個代理方法還有 newState 一個型別 SignUpCodeRequiredState為 的參數,讓你可以使用兩種新方法:

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

      要提交使用者提供的程式碼,請使用:

      newState.submitCode(code: userSuppliedCode, delegate: self)
      
      • 若要將協定作為 SignUpVerifyCodeDelegate 類別的擴充,請使用:

        extension ViewController: SignUpVerifyCodeDelegate {
            func onSignUpVerifyCodeError(error: MSAL.VerifyCodeError, newState: MSAL.SignUpCodeRequiredState?) {
                resultTextView.text = "Error verifying code: \(error.errorDescription ?? "no description")"
            }
        
            func onSignUpCompleted(newState: SignInAfterSignUpState) {
                resultTextView.text = "Signed up successfully!"
            }
        }
        

        submitCode(code:delegate) 接受一個委派參數,你必須在SignUpVerifyCodeDelegate 協議中實作所需的方法。 在最常見的情境中,你會收到一通電話 onSignUpCompleted(newState) ,表示使用者已註冊且流程完成。

在註冊期間收集用戶屬性

無論您是使用電子郵件一次性密碼或使用者名稱(電子郵件)和密碼註冊使用者,都可以在建立用戶帳戶之前收集用戶屬性。 您可以使用具有 attributes 屬性的 signUp(parameters:delegate) 來呼叫 MSALNativeAuthSignUpParameters 方法。

  1. 若要收集使用者屬性,請使用下列代碼段:

    let attributes = [
        "country": "United States",
        "city": "Redmond"
    ]
    
    let parameters = MSALNativeAuthSignUpParameters(username: email)
    parameters.password = password
    parameters.attributes = attributes
    nativeAuth.signUp(parameters: parameters, delegate: self)
    

    signUp(parameters:delegate)會導致呼叫 onSignUpCodeRequired()onSignUpStartError() 委派方法,或者如果在委派中實作,則呼叫 onSignUpAttributesInvalid(attributeNames: [String])

  2. 若要將該 SignUpStartDelegate 協定作為類別的擴充,請使用以下程式碼片段:

    extension ViewController: SignUpStartDelegate {
        func onSignUpStartError(error: MSAL.SignUpStartError) {
            resultTextView.text = "Error signing up: \(error.errorDescription ?? "no description")"
        }
    
        func onSignUpCodeRequired(
            newState: MSAL.SignUpCodeRequiredState,
            sentTo: String,
            channelTargetType: MSAL.MSALNativeAuthChannelType,
            codeLength: Int
        ) {
            resultTextView.text = "Verification code sent to \(sentTo)"
        }
    
        func onSignUpAttributesInvalid(attributeNames: [String]) {
           resultTextView.text = "Invalid attributes  \(attributeNames)"
        }
    }
    

    如果屬性無效,則會呼叫 方法 onSignUpAttributesInvalid(attributeNames: [String])。 此時,請將無效屬性清單顯示給使用者。 否則,會呼叫 onSignUpCodeRequired(newState:sentTo:channelTargetType:codeLength),表示已傳送驗證碼來驗證使用者的電子郵件地址。 除了程式碼接收者與碼數等細節外,此代理方法還有 newState 一個型別 SignUpCodeRequiredState參數,允許你使用兩種新方法:

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

跨一或多個頁面的用戶屬性

若要將屬性分散到一個或多個頁面,請將你打算在不同頁面收集的屬性設為客戶身份與存取管理(CIAM)租戶設定中的強制性。

MSALNativeAuthSignUpParameters 執行個體中呼叫 signUp(parameters:delegate) 時,不要傳入任何屬性。 下一步是打電話 newState.submitCode(code: userSuppliedCode, delegate: self) 確認使用者的電子郵件。

像之前一樣實作 SignUpVerifyCodeDelegate 該協定作為類別的擴充,但這次除了必要的方法外,還必須實作選用方法 onSignUpAttributesRequired(attributes:newState)

extension ViewController: SignUpVerifyCodeDelegate {
    func onSignUpAttributesRequired(newState: SignUpAttributesRequiredState) {
        resultTextView.text = "Attributes required"
    }

    func onSignUpVerifyCodeError(error: MSAL.VerifyCodeError, newState: MSAL.SignUpCodeRequiredState?) {
        resultTextView.text = "Error verifying code: \(error.errorDescription ?? "no description")"
    }

    func onSignUpCompleted(newState: SignInAfterSignUpState) {
        resultTextView.text = "Signed up successfully!"
    }
}

此委派方法具有一個類型為 SignUpAttributesRequiredStatenewState 參數,可讓您存取一個新方法:

  • submitAttributes(attributes:delegate)

要提交使用者提供的屬性,請使用以下程式碼片段:

let attributes = [
    "country": "United States",
    "city": "Redmond"
]

newState.submitAttributes(attributes: attributes, delegate: self)

另外,也請以擴充的方式讓你的類別實作 SignUpAttributesRequiredDelegate 協定:

extension ViewController: SignUpAttributesRequiredDelegate {
    func onSignUpAttributesRequiredError(error: AttributesRequiredError) {
        resultTextView.text = "Error submitting attributes: \(error.errorDescription ?? "no description")"
    }

    func onSignUpAttributesRequired(attributes: [MSALNativeAuthRequiredAttribute], newState: SignUpAttributesRequiredState) {
        resultTextView.text = "Attributes required"
    }

    func onSignUpAttributesInvalid(attributeNames: [String], newState: SignUpAttributesRequiredState) {
        resultTextView.text = "Attributes invalid"
    }

    func onSignUpCompleted(newState: SignInAfterSignUpState) {
        resultTextView.text = "Signed up successfully!"
    }
}

當使用者未提供所有必要的屬性,或屬性無效時,會呼叫這些委派方法:

  • onSignUpAttributesInvalid:表示發送的屬性中,有一個或多個未通過輸入驗證。 此錯誤包含 attributeNames 參數,這是開發人員傳送且輸入驗證失敗的所有屬性清單。
  • onSignUpAttributesRequired:表示伺服器需要傳送一或多個屬性,才能建立用戶帳戶。 當租用戶組態中設定一或多個屬性為必要時,就會發生這種情況。 此結果包含屬性參數,這是一份 MSALNativeAuthRequiredAttribute 物件清單,其中概述 API 所需使用者屬性的詳細數據。

這兩個委派方法都包含新的狀態參考。 用 newState 參數再次呼叫 submitAttributes(attributes:delegate) 新的屬性。

註冊時請收集用戶名(別名)

使用者名稱(別名)是一個特殊的使用者屬性。 就像城市或國家等其他屬性一樣,你會在註冊時收集這項資料。 與這些屬性不同的是,使用者之後可以使用別名登入。 別名(例如「johndoe」)讓使用者以比電子郵件地址更簡短、更友善的方式登入。

用戶名稱(別名)不會取代使用者名稱(電子郵件)。 註冊時,應用程式必須始終收集使用者名稱(電子郵件)作為主要識別碼,並將別名作為電子郵件的屬性。 登入時,使用者可以選擇使用使用者名稱(電子郵件)或使用者名稱(別名)登入。

當您在註冊使用者流程中啟用內建使用者屬性 Username 時,SDK 會透過與其他屬性相同的屬性字典來接受此屬性,並將 flatusername 作為鍵。 你可以直接在通話中 signUp 傳遞使用者名稱(別名),這樣使用者就不需要再經歷屬性所需的步驟。

要收集使用者名稱(別名),請在註冊介面中與電子郵件欄位並列輸入該使用者名稱,然後在註冊呼叫中將別名作為屬性傳遞:

guard let email = emailTextField.text, !email.isEmpty,
      let password = passwordTextField.text, !password.isEmpty,
      let username = usernameTextField.text, !username.isEmpty else {
    showResultText("Please fill in all fields")
    return
}

let attributes: [String: Any] = [
    "flatusername": username
]

let parameters = MSALNativeAuthSignUpParameters(username: email)
parameters.password = password
parameters.attributes = attributes
nativeAuth.signUp(parameters: parameters, delegate: self)

對於電子郵件一次性密碼流程(無密碼),在不設定密碼的情況下傳遞屬性:

let parameters = MSALNativeAuthSignUpParameters(username: email)
parameters.attributes = attributes
nativeAuth.signUp(parameters: parameters, delegate: self)

在處理使用者名稱(別名)註冊錯誤時,該 error.isUserAlreadyExists 屬性也會涵蓋重複別名,並 error.isInvalidAttributes 顯示一個無效的別名值。

處理註冊錯誤

註冊期間,並非每個動作都成功。 例如,使用者可能會嘗試使用已使用的電子郵件地址註冊,或提交無效的程序代碼。

在早期的協定實作 SignUpStartDelegate 中,錯誤僅在處理 onSignUpStartError(error) 代理函式時顯示。

若要藉由管理特定錯誤類型來增強使用者體驗,請使用下列代碼段:

func onSignUpStartError(error: MSAL.SignUpStartError) {
    if error.isUserAlreadyExists {
        resultTextView.text = "Unable to sign up: User already exists"
    } else if error.isInvalidPassword {
        resultTextView.text = "Unable to sign up: The password is invalid"
    } else if error.isInvalidUsername {
        resultTextView.text = "Unable to sign up: The username is invalid"
    } else {
        resultTextView.text = "Unexpected error signing up: \(error.errorDescription ?? "no description")"
    }
}

可以選擇在註冊流程之後登入。

註冊流程成功後,你可以在不啟動登入流程的情況下登入使用者。 如果使用者是以使用者名稱(alias)註冊,他們可以用電子郵件地址或別名登入。 若要深入瞭解,請參閱 教學課程:在iOS/macOS 應用程式註冊後自動登入使用者一文

下一步