เพิ่มการลงชื่อเข้าใช้ในแอป iOS ด้วย FirebaseUI

FirebaseUI สำหรับ SwiftUI เป็นไลบรารีที่ทันสมัยและใช้ SwiftUI เป็นอันดับแรก ซึ่งสร้างขึ้นบน Firebase Authentication ที่มี โฟลว์การลงชื่อเข้าใช้ที่สร้างไว้ล่วงหน้าสำหรับแอปของคุณ

FirebaseUI สำหรับ SwiftUI มีประโยชน์ดังนี้

  • UI เริ่มต้นที่กำหนด: เพิ่มขั้นตอนการลงชื่อเข้าใช้ที่สมบูรณ์ด้วย AuthPickerView
  • ปรับแต่งได้: แสดงปุ่มเริ่มต้นในเลย์เอาต์ของคุณเอง หรือสร้างประสบการณ์การใช้งานที่กำหนดเองทั้งหมด
  • การลิงก์บัญชีที่ไม่ระบุชื่อ: อัปเกรดผู้ใช้ที่ไม่ระบุชื่อแทนการแทนที่ (ไม่บังคับ)
  • การจัดการบัญชี: ขั้นตอนในตัวสำหรับการลงชื่อสมัครใช้ การกู้คืนรหัสผ่าน และการจัดการบัญชี
  • ผู้ให้บริการหลายราย: อีเมล/รหัสผ่าน ลิงก์อีเมล การตรวจสอบสิทธิ์ทางโทรศัพท์ Apple, Google, Facebook, Twitter และผู้ให้บริการ OAuth2/OIDC มาตรฐาน
  • ฟีเจอร์การตรวจสอบสิทธิ์สมัยใหม่: รองรับการตรวจสอบสิทธิ์แบบหลายปัจจัย (MFA) และ API แบบอะซิงโครนัส/รอ

ก่อนเริ่มต้น

การติดตั้ง

FirebaseUI สำหรับ SwiftUI มีให้ใช้งานเป็นแพ็กเกจ Swift หากต้องการติดตั้งแพ็กเกจในโปรเจ็กต์ Xcode ให้ทำดังนี้

  1. ใน Xcode ให้คลิกไฟล์ > เพิ่มการขึ้นต่อกันของแพ็กเกจ

  2. ป้อน URL ของแพ็กเกจ

    https://github.com/firebase/FirebaseUI-iOS
    
  3. ในเมนูกฎการขึ้นต่อกัน ให้เลือกสูงสุดถึงเวอร์ชันหลักถัดไป แล้วตั้งค่า เวอร์ชันขั้นต่ำเป็น รุ่นล่าสุด

  4. คลิกเพิ่มแพ็กเกจ แล้วเลือกไลบรารีที่จะเพิ่มลงในโปรเจ็กต์

    ต้องมีไลบรารีต่อไปนี้เสมอ ซึ่งรวมถึงการอ้างอิงหลัก ตลอดจนการรองรับการลงชื่อเข้าใช้ด้วยอีเมลและรหัสผ่าน รวมถึงการลงชื่อเข้าใช้ด้วยลิงก์อีเมล

    • FirebaseAuthSwiftUI

     หากต้องการรองรับวิธีการลงชื่อเข้าใช้อื่นๆ ให้เลือกไลบรารีต่อไปนี้อย่างน้อย 1 รายการด้วย

    • FirebaseAppleSwiftUI (ลงชื่อเข้าใช้ด้วย Apple)
    • FirebaseGoogleSwiftUI (ลงชื่อเข้าใช้ด้วย Google)
    • FirebaseFacebookSwiftUI (ลงชื่อเข้าใช้ด้วย Facebook)
    • FirebasePhoneAuthSwiftUI (การตรวจสอบสิทธิ์ทางโทรศัพท์)
    • FirebaseTwitterSwiftUI (ลงชื่อเข้าใช้ด้วย Twitter)
    • FirebaseOAuthSwiftUI (ผู้ให้บริการ OAuth และ OIDC มาตรฐาน เช่น GitHub, Microsoft, Yahoo)
  5. คลิกเพิ่มแพ็กเกจเพื่อติดตั้งไลบรารีที่เลือก

  6. กำหนดค่า AuthService ของ FirebaseUI ในตัวเริ่มต้นของ View ระดับบนสุดและส่งบริการไปยังมุมมองย่อยโดยใช้สภาพแวดล้อม

    import FirebaseAuthSwiftUI
    import SwiftUI
    
    struct ContentView: View {
      let authService: AuthService
    
      init() {
        let configuration = AuthConfiguration()
    
        authService = AuthService(configuration: configuration)
          .withEmailSignIn() // Or whatever sign-in methods you want to support.
                             // See the next section.
      }
    
      var body: some View {
        AuthPickerView { // AuthPickerView (the prebuilt View) or a custom View.
            Text("Welcome to your app!")
        }
        .environment(authService)
      }
    }
    

ตั้งค่าวิธีการลงชื่อเข้าใช้

วิธีการลงชื่อเข้าใช้ที่รองรับแต่ละวิธีต้องมีขั้นตอนการตั้งค่าเพิ่มเติม ขยายส่วนด้านล่างและทําตามวิธีการเพื่อตั้งค่าผู้ให้บริการแต่ละราย

อีเมลและรหัสผ่าน

  1. จากส่วนความปลอดภัย > การตรวจสอบสิทธิ์ > วิธีการลงชื่อเข้าใช้ ของFirebaseคอนโซล ให้เปิดใช้ผู้ให้บริการอีเมล/รหัสผ่าน

  2. ลงทะเบียนผู้ให้บริการในอินสแตนซ์ AuthService ดังนี้

    let authService = AuthService()
      .withEmailSignIn()
    

วิธีใช้การลงชื่อเข้าใช้ด้วยลิงก์อีเมลแบบไม่ต้องใช้รหัสผ่าน

  1. จากส่วนความปลอดภัย > การตรวจสอบสิทธิ์ > วิธีการลงชื่อเข้าใช้ ของคอนโซล Firebase ให้เปิดใช้อีเมล/รหัสผ่าน แล้วเปิดใช้ การลงชื่อเข้าใช้ด้วยลิงก์อีเมล โปรดทราบว่าต้องเปิดใช้การลงชื่อเข้าใช้ด้วยอีเมลหรือรหัสผ่านก่อนจึงจะ ใช้การลงชื่อเข้าใช้ด้วยลิงก์อีเมลได้

  2. เพิ่มโดเมนลิงก์ไปยังโดเมนที่ได้รับอนุญาต

  3. กำหนดค่า ActionCodeSettings ส่งไปยัง AuthConfiguration และ ลงทะเบียนผู้ให้บริการ

    let actionCodeSettings = ActionCodeSettings()
    actionCodeSettings.handleCodeInApp = true
    actionCodeSettings.url = URL(string: "https://yourapp.firebaseapp.com")
    
    guard let bundleID = Bundle.main.bundleIdentifier else {
      fatalError("Missing bundle identifier for email link authentication setup.")
    }
    actionCodeSettings.setIOSBundleID(bundleID)
    
    let configuration = AuthConfiguration(
      emailLinkSignInActionCodeSettings: actionCodeSettings
    )
    
    let authService = AuthService(configuration: configuration)
      .withEmailLinkSignIn()
    
  4. ใน AppDelegate เดียวกันกับที่เรียกใช้ FirebaseApp.configure() ให้เพิ่ม ตรรกะลงในเมธอด application(_:open:options:) ที่แสดงผล true เมื่อ URL ที่เปิดเป็นลิงก์ลงชื่อเข้าใช้ Firebase Authentication ตรรกะนี้ บ่งชี้ว่า FirebaseUI จัดการลิงก์แล้ว และไม่ควรประมวลผล โดยตัวแฮนเดิลลิงก์อื่นๆ

    import FacebookCore
    import FirebaseAuth
    import UIKit
    
    class AppDelegate: NSObject, UIApplicationDelegate {
      func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
      ) -> Bool {
        FirebaseApp.configure()
        return true
      }
    
      func application(
        _ application: UIApplication,
        open url: URL,
        options: [UIApplication.OpenURLOptionsKey: Any] = [:]
      ) -> Bool {
        if Auth.auth().canHandle(url) {
          return true
        }
        return false
      }
    }
    
  5. หากสร้างมุมมองที่กำหนดเอง ให้เรียกใช้ authService.handleSignInLink(url:) เมื่อลิงก์เปิดแอป

Apple

วิธีใช้ฟีเจอร์ลงชื่อเข้าใช้ด้วย Apple

  1. วิธีกำหนดค่าการลงชื่อเข้าใช้ด้วย Apple

    1. เปิดใช้การลงชื่อเข้าใช้ด้วย Apple สำหรับแอปในหน้าใบรับรอง ตัวระบุ และโปรไฟล์ ของเว็บไซต์นักพัฒนาแอปของ Apple
    2. เชื่อมโยงเว็บไซต์กับแอปตามที่อธิบายไว้ในส่วนแรกของกำหนดค่าการลงชื่อเข้าใช้ด้วย Apple สำหรับเว็บ เมื่อได้รับข้อความแจ้ง ให้ลงทะเบียน URL ต่อไปนี้เป็น URL เปลี่ยนเส้นทางกลับ
      https://YOUR_FIREBASE_PROJECT_ID.firebaseapp.com/__/auth/handler
      คุณดูรหัสโปรเจ็กต์ Firebase ได้ในหน้าการตั้งค่าคอนโซลFirebase เมื่อเสร็จแล้ว ให้จดรหัสบริการใหม่ ซึ่งคุณจะต้องใช้ใน ส่วนถัดไป
    3. สร้างคีย์ส่วนตัวสำหรับลงชื่อเข้าใช้ด้วย Apple คุณจะต้องใช้คีย์ส่วนตัวและรหัสคีย์ใหม่ในส่วนถัดไป
    4. หากคุณใช้ฟีเจอร์ของ Firebase Authentication ที่ส่งอีเมลถึงผู้ใช้ รวมถึงการลงชื่อเข้าใช้ด้วยลิงก์อีเมล การยืนยันอีเมล การเพิกถอนการเปลี่ยนแปลงบัญชี และอื่นๆ ให้กำหนดค่าบริการส่งต่ออีเมลส่วนตัวของ Apple และลงทะเบียน noreply@YOUR_FIREBASE_PROJECT_ID.firebaseapp.com (หรือโดเมนเทมเพลตอีเมลที่กำหนดเอง) เพื่อให้ Apple ส่งต่ออีเมลที่ Firebase Authentication ส่งไปยังอีเมล Apple ที่ไม่ระบุตัวตนได้
  2. จากส่วนความปลอดภัย > การตรวจสอบสิทธิ์ > วิธีการลงชื่อเข้าใช้ ของคอนโซล Firebase ให้เปิดใช้ Apple

    • ระบุรหัสบริการที่คุณสร้างไว้ในส่วนก่อนหน้า
    • ในส่วนการกำหนดค่าโฟลว์ของรหัส OAuth ให้ระบุรหัสทีม Apple คีย์ส่วนตัว และรหัสคีย์ที่คุณสร้างไว้ในส่วนก่อนหน้า
  3. ใน Xcode ให้เปิดส่วนการลงนามและความสามารถของโปรแกรมแก้ไขโปรเจ็กต์ แล้วเพิ่มความสามารถลงชื่อเข้าใช้ด้วย Apple

  4. ลงทะเบียนผู้ให้บริการในอินสแตนซ์ AuthService ดังนี้

    let authService = AuthService()
      .withAppleSignIn()
    

Google

วิธีใช้การลงชื่อเข้าใช้ด้วย Google

  1. จากส่วนความปลอดภัย > การตรวจสอบสิทธิ์ > วิธีการลงชื่อเข้าใช้ ของคอนโซล Firebase ให้เปิดใช้ผู้ให้บริการ Google

  2. ดาวน์โหลดสำเนาใหม่ของไฟล์ GoogleService-Info.plist ของโปรเจ็กต์ แล้วคัดลอกไปยังโปรเจ็กต์ Xcode เขียนทับเวอร์ชันที่มีอยู่ด้วยเวอร์ชันใหม่

  3. เพิ่มรูปแบบ URL ที่กำหนดเองลงในโปรเจ็กต์ Xcode โดยทำดังนี้

    1. เปิดการกำหนดค่าโปรเจ็กต์โดยคลิกชื่อโปรเจ็กต์ในมุมมองแบบทรีทางด้านซ้าย เลือกแอปจากส่วนเป้าหมาย จากนั้น เลือกแท็บข้อมูล แล้วขยายส่วนประเภท URL

    2. คลิกปุ่ม + แล้วเพิ่มรูปแบบ URL สำหรับรหัสไคลเอ็นต์ที่กลับด้าน หากต้องการดูค่านี้ ให้เปิด GoogleService-Info.plistไฟล์การกำหนดค่า แล้วมองหาคีย์ REVERSED_CLIENT_ID คัดลอกค่าของคีย์นั้น แล้ววางลงในช่องรูปแบบ URL ในหน้าการกำหนดค่า ไม่ต้องแก้ไขฟิลด์อื่นๆ

      เมื่อเสร็จแล้ว การกำหนดค่าควรมีลักษณะคล้ายกับ ต่อไปนี้ (แต่มีค่าเฉพาะแอปพลิเคชันของคุณ)

  4. ลงทะเบียนผู้ให้บริการในอินสแตนซ์ AuthService ดังนี้

    let authService = AuthService()
      .withGoogleSignIn()
    

Facebook

วิธีใช้การเข้าสู่ระบบด้วย Facebook

  1. ตั้งค่า Facebook Login สำหรับ iOS SDK โดยทำตามวิธีการ ในเว็บไซต์ Meta for Developers ข้ามขั้นตอนสุดท้าย "เพิ่มการเข้าสู่ระบบด้วย Facebook ลงในโค้ด"

  2. จากส่วนความปลอดภัย > การตรวจสอบสิทธิ์ > วิธีการลงชื่อเข้าใช้ ของคอนโซล Firebase ให้เปิดใช้ผู้ให้บริการ Facebook คุณจะต้องใช้รหัสแอป Facebook และข้อมูลลับของแอปจากเว็บไซต์ Meta for Developers

  3. ลงทะเบียนผู้ให้บริการในอินสแตนซ์ AuthService ดังนี้

    let authService = AuthService()
     .withFacebookSignIn()
    

หมายเลขโทรศัพท์

วิธีใช้การตรวจสอบสิทธิ์ทางโทรศัพท์

  1. จากส่วนความปลอดภัย > การตรวจสอบสิทธิ์ > วิธีการลงชื่อเข้าใช้ ของFirebaseคอนโซล ให้เปิดใช้ผู้ให้บริการโทรศัพท์

  2. กำหนดค่า APNs สำหรับแอปตามที่ระบุไว้ในส่วนเริ่มรับการแจ้งเตือนแบบเงียบ

  3. เพิ่มรูปแบบ URL ที่กำหนดเองลงในโปรเจ็กต์ Xcode โดยทำดังนี้

    1. เปิดการกำหนดค่าโปรเจ็กต์โดยคลิกชื่อโปรเจ็กต์ในมุมมองแบบทรีทางด้านซ้าย เลือกแอปจากส่วนเป้าหมาย จากนั้น เลือกแท็บข้อมูล แล้วขยายส่วนประเภท URL

    2. คลิกปุ่ม + แล้วเพิ่มรหัสแอปที่เข้ารหัสเป็นรูปแบบ URL หากต้องการดูค่านี้ ให้เปิดการตั้งค่า > ทั่วไป ในคอนโซล Firebase

      เมื่อเสร็จแล้ว การกำหนดค่าควรมีลักษณะคล้ายกับ ต่อไปนี้ (แต่มีค่าเฉพาะแอปพลิเคชันของคุณ)

  4. ใน AppDelegate เดียวกันกับที่คุณเรียก FirebaseApp.configure() ให้เพิ่ม ตัวแฮนเดิลโทเค็น APNs ดังนี้

    import FacebookCore
    import FirebaseAuth
    import UIKit
    
    class AppDelegate: NSObject, UIApplicationDelegate {
      func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
      ) -> Bool {
        FirebaseApp.configure()
        return true
      }
    
      func application(
        _ application: UIApplication,
        didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
      ) {
        #if DEBUG
        Auth.auth().setAPNSToken(deviceToken, type: .sandbox)
        #else
        Auth.auth().setAPNSToken(deviceToken, type: .prod)
        #endif
      }
    }
    
  5. ลงทะเบียนผู้ให้บริการในอินสแตนซ์ AuthService ดังนี้

    let authService = AuthService()
      .withPhoneSignIn()
    

Twitter (X)

วิธีใช้การเข้าสู่ระบบด้วย Twitter

  1. สร้างข้อมูลเข้าสู่ระบบ X API โดยทำตามวิธีการใน เว็บไซต์นักพัฒนาซอฟต์แวร์ X

  2. จากส่วนความปลอดภัย > การตรวจสอบสิทธิ์ > วิธีการลงชื่อเข้าใช้ ของFirebaseคอนโซล ให้เปิดใช้ผู้ให้บริการ Twitter คุณจะต้องใช้คีย์ API และ API Secret จากคอนโซลนักพัฒนาซอฟต์แวร์ของ X

  3. ลงทะเบียนผู้ให้บริการในอินสแตนซ์ AuthService ดังนี้

    let authService = AuthService()
      .withTwitterSignIn()
    

ผู้ให้บริการ OAuth2 และ OIDC มาตรฐาน

นอกจากนี้ FirebaseUI ยังรองรับผู้ให้บริการ OAuth ในตัว เช่น GitHub, Microsoft และ Yahoo รวมถึงผู้ให้บริการ OIDC ที่กำหนดเองซึ่งกำหนดค่าไว้ในการตรวจสอบสิทธิ์ Firebase

 let authService = AuthService()
  .withOAuthSignIn(OAuthProviderSwift.github())
  .withOAuthSignIn(OAuthProviderSwift.microsoft())
  .withOAuthSignIn(OAuthProviderSwift.yahoo())

สำหรับผู้ให้บริการ OIDC ที่กำหนดเอง ให้กำหนดค่าผู้ให้บริการใน Firebase Authentication ก่อน จากนั้นสร้าง OAuthProviderSwift โดยใช้รหัสผู้ให้บริการและการกำหนดค่าปุ่ม

let lineProvider = OAuthProviderSwift(
  providerId: "oidc.line",
  buttonLabel: "Sign in with LINE",
  displayName: "LINE",
  iconSystemName: "person.crop.circle.badge.checkmark",
  buttonBackgroundColor: .green,
  buttonForegroundColor: .white
)

let authService = AuthService()
  .withOAuthSignIn(lineProvider)

การใช้มุมมองการตรวจสอบสิทธิ์ที่สร้างไว้ล่วงหน้า

FirebaseUI สำหรับ SwiftUI มี AuthPickerView ซึ่งเป็น UI การตรวจสอบสิทธิ์ที่สร้างไว้ล่วงหน้าและมีข้อจำกัด ซึ่งจะจัดการโฟลว์การตรวจสอบสิทธิ์ทั้งหมดให้คุณ นี่เป็นวิธีที่ง่ายที่สุดในการเพิ่มการตรวจสอบสิทธิ์ลงในแอป

ตัวอย่าง

ต่อไปนี้คือตัวอย่างการใช้งาน AuthPickerView ที่มีผู้ให้บริการหลายรายและ ตัวเลือกการกำหนดค่า

import FirebaseAppleSwiftUI
import FirebaseAuthSwiftUI
import FirebaseGoogleSwiftUI
import SwiftUI

struct ContentView: View {
  let authService: AuthService

  init() {
    // Create configuration with options
    let configuration = AuthConfiguration(
      tosUrl: URL(string: "https://example.com/tos"),
      privacyPolicyUrl: URL(string: "https://example.com/privacy"),
      shouldAutoUpgradeAnonymousUsers: true
    )

    // Initialize AuthService with multiple providers
    authService = AuthService(configuration: configuration)
      .withEmailSignIn()
      .withAppleSignIn()
      .withGoogleSignIn()
  }

  var body: some View {
    AuthPickerView {
      authenticatedContent
    }
    .environment(authService)
  }

  var authenticatedContent: some View {
    NavigationStack {
      VStack(spacing: 20) {
        if authService.authenticationState == .authenticated {
          Text("Authenticated")
          
          Button("Manage Account") {
            authService.isPresented = true
          }
          .buttonStyle(.bordered)
          
          Button("Sign Out") {
            Task {
              try? await authService.signOut()
            }
          }
          .buttonStyle(.borderedProminent)
        } else {
          Text("Not Authenticated")
          
          Button("Sign In") {
            authService.isPresented = true
          }
          .buttonStyle(.borderedProminent)
        }
      }
      .navigationTitle("My App")
    }
    .onChange(of: authService.authenticationState) { _, newValue in
      // Automatically show auth UI when not authenticated
      if newValue != .authenticating {
        authService.isPresented = (newValue == .unauthenticated)
      }
    }
  }
}

การปรับแต่งทั่วไป

แม้ว่าพารามิเตอร์ทั้งหมดของ AuthConfiguration จะไม่บังคับ แต่แอปส่วนใหญ่ จะปรับแต่งการตั้งค่าต่อไปนี้อย่างน้อย

let configuration = AuthConfiguration(
    logo: ImageResource.exampleLogoAsset,
    customStringsBundle: .main,
    tosUrl: URL(string: "https://example.com/tos"),
    privacyPolicyUrl: URL(string: "https://example.com/privacy"),
)
  • logo: รูปภาพโลโก้ที่แสดงในเอกสารการตรวจสอบสิทธิ์ ดูข้อมูลเกี่ยวกับการรวมชิ้นงานรูปภาพของคุณเองได้ที่ การเพิ่มรูปภาพลงในโปรเจ็กต์ Xcode

  • customStringsBundle: ใช้สตริงที่กำหนดเองจาก Bundle ที่ระบุ ซึ่งใช้สำหรับการแปลภาษาท้องถิ่น รวมถึงใช้ปรับแต่งสตริงเริ่มต้นที่ AuthPickerView ใช้ด้วย เช่น หากต้องการตั้งค่าข้อความที่แสดงที่ด้านบนของชีตการตรวจสอบสิทธิ์ ให้สร้าง Localizable.strings ที่มีเนื้อหาต่อไปนี้

    "Sign in with Firebase" = "Sign in to use ExampleApp";
    

ข้อมูลที่รวมอยู่ในมุมมองที่สร้างไว้ล่วงหน้า

เมื่อใช้ AuthPickerView คุณจะได้รับสิ่งต่อไปนี้

  1. งานนำเสนอชีต: UI การตรวจสอบสิทธิ์จะปรากฏเป็นชีตแบบโมดัล
  2. การนำทางในตัว: การนำทางอัตโนมัติระหว่างหน้าจอลงชื่อเข้าใช้ การกู้คืนรหัสผ่าน MFA ลิงก์อีเมล และการยืนยันทางโทรศัพท์
  3. การจัดการสถานะการตรวจสอบสิทธิ์: สลับระหว่าง UI การตรวจสอบสิทธิ์กับเนื้อหาโดยอัตโนมัติตาม authService.authenticationState
  4. ควบคุมผ่าน isPresented: ควบคุมเวลาที่ชีตการให้สิทธิ์จะปรากฏโดยการตั้งค่า authService.isPresented = true/false

ลักษณะการทำงานที่กำหนดเอง

AuthPickerViewเริ่มต้นจะกำหนดวิธีจัดการกับสถานการณ์ที่ซับซ้อนหลายอย่าง ดังนี้

1. การแก้ไขความขัดแย้งของบัญชี

เมื่อเกิดข้อขัดแย้งของบัญชี (เช่น การลงชื่อเข้าใช้ด้วยข้อมูลเข้าสู่ระบบที่ลิงก์กับบัญชีอื่นอยู่แล้ว) AuthPickerView จะจัดการโดยอัตโนมัติ ดังนี้

  • การอัปเกรดแบบไม่ระบุตัวตนที่ขัดแย้งกัน: หากเปิดใช้ shouldAutoUpgradeAnonymousUsers และเกิดความขัดแย้งระหว่างการอัปเกรดแบบไม่ระบุตัวตน ระบบจะออกจากระบบผู้ใช้ที่ไม่ระบุตัวตนโดยอัตโนมัติและลงชื่อเข้าใช้ด้วยข้อมูลเข้าสู่ระบบใหม่
  • ความขัดแย้งอื่นๆ: สำหรับความขัดแย้งของข้อมูลเข้าสู่ระบบระหว่างบัญชีที่ไม่ใช่บัญชีที่ไม่ระบุตัวตน ระบบจะจัดเก็บข้อมูลเข้าสู่ระบบที่รอดำเนินการและพยายามลิงก์หลังจากลงชื่อเข้าใช้สำเร็จ

ซึ่งจัดการโดย AccountConflictModifier ที่ใช้ที่ระดับ NavigationStack

2. การตรวจสอบสิทธิ์แบบหลายปัจจัย (MFA)

เมื่อเปิดใช้ MFA ในการกำหนดค่า

  • ตรวจหาโดยอัตโนมัติเมื่อต้องใช้ MFA ระหว่างการลงชื่อเข้าใช้
  • แสดงหน้าจอการแก้ปัญหา MFA ที่เหมาะสม (SMS หรือ TOTP)
  • จัดการขั้นตอนการลงทะเบียนและการจัดการ MFA
  • รองรับทั้งปัจจัยรหัสผ่านแบบใช้ครั้งเดียว (OTP) ที่อิงตาม SMS และตามเวลา (TOTP)
3. การจัดการข้อผิดพลาด

มุมมองเริ่มต้นมีการจัดการข้อผิดพลาดในตัวดังนี้

  • แสดงข้อความแสดงข้อผิดพลาดที่ใช้งานง่ายในกล่องโต้ตอบการแจ้งเตือน
  • กรองข้อผิดพลาดที่จัดการภายในโดยอัตโนมัติ (เช่น ข้อผิดพลาดในการยกเลิก ความขัดแย้งที่จัดการโดยอัตโนมัติ)
  • ใช้ข้อความแสดงข้อผิดพลาดที่แปลแล้วผ่าน StringUtils
  • ข้อผิดพลาดจะเผยแพร่ผ่านคีย์สภาพแวดล้อม reportError

เมื่อกำหนดค่าการลงชื่อเข้าใช้ด้วยลิงก์อีเมลแล้ว

  • จัดเก็บอีเมลในพื้นที่เก็บข้อมูลของแอปโดยอัตโนมัติ
  • จัดการการนำทาง Deep Link จากอีเมล
  • จัดการขั้นตอนการยืนยันอีเมลทั้งหมด
  • รองรับการอัปเกรดผู้ใช้ที่ไม่ระบุตัวตนผ่านลิงก์ในอีเมล
5. การอัปเกรดอัตโนมัติสำหรับผู้ใช้ที่ไม่ระบุชื่อ

เมื่อเปิดใช้ shouldAutoUpgradeAnonymousUsers

  • พยายามลิงก์บัญชีที่ไม่ระบุตัวตนกับข้อมูลเข้าสู่ระบบใหม่โดยอัตโนมัติ
  • เก็บรักษาข้อมูลผู้ใช้โดยการอัปเกรดแทนการแทนที่เซสชันที่ไม่ระบุตัวตน
  • จัดการข้อขัดแย้งในการอัปเกรดแฮนเดิลอย่างราบรื่น
6. การตรวจสอบสิทธิ์อีกครั้งในมุมมองเริ่มต้น

การดำเนินการที่มีความละเอียดอ่อน เช่น การลบบัญชี การอัปเดตรหัสผ่าน หรือการยกเลิกการลงทะเบียนปัจจัย MFA จะต้องมีการตรวจสอบสิทธิ์ล่าสุด เมื่อใช้มุมมองเริ่มต้น ระบบจะจัดการการตรวจสอบสิทธิ์อีกครั้งโดยอัตโนมัติตามผู้ให้บริการลงชื่อเข้าใช้ของผู้ใช้

เมื่อการดำเนินการที่มีความละเอียดอ่อนต้องมีการตรวจสอบสิทธิ์อีกครั้ง มุมมองเริ่มต้นจะดำเนินการต่อไปนี้โดยอัตโนมัติ

  • ผู้ให้บริการ OAuth (Google, Apple, Facebook, Twitter ฯลฯ): แสดงการแจ้งเตือนที่ขอให้ผู้ใช้ยืนยัน จากนั้นรับข้อมูลเข้าสู่ระบบใหม่โดยอัตโนมัติและดำเนินการให้เสร็จสมบูรณ์

  • อีเมล/รหัสผ่าน: แสดงชีตที่แจ้งให้ผู้ใช้ป้อนรหัสผ่านก่อนดำเนินการต่อ

  • ลิงก์อีเมล: แสดงการแจ้งเตือนที่ขอให้ส่งอีเมลยืนยัน จากนั้นแสดงชีตพร้อมวิธีการตรวจสอบอีเมล ผู้ใช้แตะลิงก์ในอีเมลเพื่อทำการตรวจสอบสิทธิ์อีกครั้งให้เสร็จสมบูรณ์

  • โทรศัพท์: แสดงการแจ้งเตือนที่อธิบายว่าต้องมีการยืนยัน จากนั้นแสดงชีตสำหรับการยืนยันรหัส SMS

การดำเนินการจะลองอีกครั้งโดยอัตโนมัติหลังจากตรวจสอบสิทธิ์อีกครั้งสำเร็จ ไม่จำเป็นต้องใช้โค้ดเพิ่มเติมเมื่อใช้ AuthPickerView หรือมุมมองการจัดการบัญชีในตัว (UpdatePasswordView, SignedInView ฯลฯ)

ขั้นสูง: สร้างมุมมองการให้สิทธิ์ที่กำหนดเอง

หากต้องการควบคุม UI หรือโฟลว์การนำทางมากขึ้น คุณสามารถสร้างมุมมองการตรวจสอบสิทธิ์ที่กำหนดเองของคุณเองได้ในขณะที่ยังคงใช้ประโยชน์จาก AuthService สำหรับตรรกะการตรวจสอบสิทธิ์

คุณสามารถผสมองค์ประกอบของ FirebaseUI กับตรรกะที่กำหนดเองได้หลายวิธี ส่วนต่อไปนี้ มีตัวอย่างแนวทางการปรับแต่งบางส่วนที่คุณอาจใช้

วิธีที่ 1: ปุ่มที่กำหนดเองพร้อม registerProvider()

หากต้องการควบคุมลักษณะที่ปรากฏของปุ่มอย่างสมบูรณ์ คุณสามารถสร้างการติดตั้งใช้งาน AuthProviderUI ที่กำหนดเองซึ่งครอบคลุมผู้ให้บริการรายใดก็ได้และแสดงผลมุมมองปุ่มที่กำหนดเอง

การสร้าง UI ของผู้ให้บริการที่กำหนดเอง

ตัวอย่างวิธีสร้างปุ่ม Twitter ที่กำหนดเองมีดังนี้

import FirebaseAuthSwiftUI
import FirebaseTwitterSwiftUI
import SwiftUI

// Step 1: Create your custom button view
struct CustomTwitterButton: View {
  let provider: TwitterProviderSwift
  @Environment(AuthService.self) private var authService
  @Environment(\.mfaHandler) private var mfaHandler

  var body: some View {
    Button {
      Task {
        do {
          let outcome = try await authService.signIn(provider)

          // Handle MFA if required
          if case let .mfaRequired(mfaInfo) = outcome,
             let onMFA = mfaHandler {
            onMFA(mfaInfo)
          }
        } catch {
          // Do Something Else
        }
      }
    } label: {
      HStack { // Your custom icon
        Text("Sign in with Twitter")
          .fontWeight(.semibold)
      }
      .frame(maxWidth: .infinity)
      .padding()
      .background(
        LinearGradient(
          colors: [Color.blue, Color.cyan],
          startPoint: .leading,
          endPoint: .trailing
        )
      )
      .foregroundColor(.white)
      .cornerRadius(12)
      .shadow(radius: 4)
    }
  }
}

// Step 2: Create a custom AuthProviderUI wrapper
class CustomTwitterProviderAuthUI: AuthProviderUI {
  private let typedProvider: TwitterProviderSwift
  var provider: AuthProviderSwift { typedProvider }
  let id: String = "twitter.com"

  init(provider: TwitterProviderSwift = TwitterProviderSwift()) {
    typedProvider = provider
  }

  @MainActor func authButton() -> AnyView {
    AnyView(CustomTwitterButton(provider: typedProvider))
  }
}

// Step 3: Use it in your app
struct ContentView: View {
  let authService: AuthService

  init() {
    let configuration = AuthConfiguration()
    authService = AuthService(configuration: configuration)

    // Register your custom provider UI
    authService.registerProvider(
      providerWithButton: CustomTwitterProviderAuthUI()
    )
    authService.isPresented = true
  }

  var body: some View {
    AuthPickerView {
      usersApp
    }
    .environment(authService)
  }

  var usersApp: some View {
    NavigationStack {
      VStack {
        Button {
          authService.isPresented = true
        } label: {
          Text("Authenticate")
        }
      }
    }
  }
}

ตัวอย่างปุ่มที่กำหนดเองแบบง่าย

นอกจากนี้ คุณยังสร้างปุ่มที่กำหนดเองที่เรียบง่ายกว่าสำหรับผู้ให้บริการรายใดก็ได้ โดยทำดังนี้

import FirebaseAuthSwiftUI
import FirebaseGoogleSwiftUI
import FirebaseAppleSwiftUI
import SwiftUI

// Custom Google Provider UI
class CustomGoogleProviderAuthUI: AuthProviderUI {
  private let typedProvider: GoogleProviderSwift
  var provider: AuthProviderSwift { typedProvider }
  let id: String = "google.com"
  
  init() {
    typedProvider = GoogleProviderSwift()
  }
  
  @MainActor func authButton() -> AnyView {
    AnyView(CustomGoogleButton(provider: typedProvider))
  }
}

struct CustomGoogleButton: View {
  let provider: GoogleProviderSwift
  @Environment(AuthService.self) private var authService
  
  var body: some View {
    Button {
      Task {
        try? await authService.signIn(provider)
      }
    } label: {
      HStack {
        Image(systemName: "g.circle.fill")
        Text("My Custom Google Button")
      }
      .frame(maxWidth: .infinity)
      .padding()
      .background(Color.purple) // Your custom color
      .foregroundColor(.white)
      .cornerRadius(10)
    }
  }
}

// Then use it
struct ContentView: View {
  let authService: AuthService
  
  init() {
    let configuration = AuthConfiguration()
    authService = AuthService(configuration: configuration)
      .withAppleSignIn() // Use default Apple button
    
    // Use custom Google button
    authService.registerProvider(
      providerWithButton: CustomGoogleProviderAuthUI()
    )
  }
  
  var body: some View {
    AuthPickerView {
      Text("App Content")
    }
    .environment(authService)
  }
}

วิธีนี้ใช้ได้กับผู้ให้บริการทุกราย ได้แก่ Google, Apple, Twitter, Facebook, โทรศัพท์ และผู้ให้บริการ OAuth เพียงสร้างมุมมองปุ่มที่กำหนดเองแล้วห่อหุ้มไว้ในคลาสที่สอดคล้องกับ AuthProviderUI

วิธีที่ 2: ปุ่มเริ่มต้นที่มีมุมมองที่กำหนดเอง

คุณใช้ AuthService.renderButtons() และข้าม AuthPickerView เพื่อแสดงปุ่มการตรวจสอบสิทธิ์เริ่มต้นขณะที่ระบุเลย์เอาต์และการนำทางของคุณเองได้โดยทำดังนี้

import FirebaseAuthSwiftUI
import FirebaseGoogleSwiftUI
import FirebaseAppleSwiftUI
import SwiftUI

struct CustomAuthView: View {
  @Environment(AuthService.self) private var authService

  var body: some View {
    VStack(spacing: 30) {
      // Your custom logo/branding
      Image("app-logo")
        .resizable()
        .frame(width: 150, height: 150)
      
      Text("Welcome to My App")
        .font(.largeTitle)
        .fontWeight(.bold)
      
      Text("Sign in to continue")
        .font(.subheadline)
        .foregroundStyle(.secondary)
      
      // Render default auth buttons
      authService.renderButtons(spacing: 12)
        .padding()
    }
    .padding()
  }
}

struct ContentView: View {
  init() {
    let configuration = AuthConfiguration()
    
    authService = AuthService(configuration: configuration)
      .withGoogleSignIn()
      .withAppleSignIn()
  }
  
  let authService: AuthService

  var body: some View {
    NavigationStack {
      if authService.authenticationState == .authenticated {
        Text("Authenticated!")
      } else {
        CustomAuthView()
      }
    }
    .environment(authService)
  }
}

แนวทางที่ 3: มุมมองที่กำหนดเองพร้อมการนำทางที่กำหนดเอง

หากต้องการควบคุมทั้งโฟลว์อย่างเต็มที่ คุณสามารถข้าม AuthPickerView และสร้างระบบนำทางของคุณเองได้โดยทำดังนี้

import FirebaseAuth
import FirebaseAuthSwiftUI
import FirebaseGoogleSwiftUI
import SwiftUI

enum CustomAuthRoute {
  case signIn
  case phoneVerification
  case mfaResolution
}

struct ContentView: View {
  private let authService: AuthService
  @State private var navigationPath: [CustomAuthRoute] = []
  @State private var errorMessage: String?

  init() {
    let configuration = AuthConfiguration()
    self.authService = AuthService(configuration: configuration)
      .withGoogleSignIn()
      .withPhoneSignIn()
  }

  var body: some View {
    NavigationStack(path: $navigationPath) {
      Group {
        if authService.authenticationState == .authenticated {
          authenticatedView
        } else {
          customSignInView
        }
      }
      .navigationDestination(for: CustomAuthRoute.self) { route in
        switch route {
        case .signIn:
          customSignInView
        case .phoneVerification:
          customPhoneVerificationView
        case .mfaResolution:
          customMFAView
        }
      }
    }
    .environment(authService)
    .alert("Error", isPresented: .constant(errorMessage != nil)) {
      Button("OK") {
        errorMessage = nil
      }
    } message: {
      Text(errorMessage ?? "")
    }
  }

  var customSignInView: some View {
    VStack(spacing: 20) {
      Text("Custom Sign In")
        .font(.title)
      
      Button("Sign in with Google") {
        Task {
          do {
            let provider = GoogleProviderSwift(clientID: Auth.auth().app?.options.clientID ?? "")
            let outcome = try await authService.signIn(provider)
            
            // Handle MFA if required
            if case .mfaRequired = outcome {
              navigationPath.append(.mfaResolution)
            }
          } catch {
            errorMessage = error.localizedDescription
          }
        }
      }
      .buttonStyle(.borderedProminent)
      
      Button("Phone Sign In") {
        navigationPath.append(.phoneVerification)
      }
      .buttonStyle(.bordered)
    }
    .padding()
  }

  var customPhoneVerificationView: some View {
    Text("Custom Phone Verification View")
    // Implement your custom phone auth UI here
  }

  var customMFAView: some View {
    Text("Custom MFA Resolution View")
    // Implement your custom MFA UI here
  }

  var authenticatedView: some View {
    VStack(spacing: 20) {
      Text("Welcome!")
      Text("Email: \(authService.currentUser?.email ?? "N/A")")
      
      Button("Sign Out") {
        Task {
          try? await authService.signOut()
        }
      }
      .buttonStyle(.borderedProminent)
    }
  }
}

ข้อควรพิจารณาที่สำคัญสำหรับมุมมองที่กำหนดเอง

เมื่อสร้างมุมมองที่กำหนดเอง คุณต้องจัดการหลายอย่างด้วยตนเองซึ่ง AuthPickerView จะจัดการโดยอัตโนมัติ ดังนี้

  1. ความขัดแย้งของบัญชี: ใช้กลยุทธ์การแก้ปัญหาความขัดแย้งของคุณเองโดยใช้ AuthServiceError.accountConflict
  2. การจัดการ MFA: ตรวจสอบ SignInOutcome สำหรับ .mfaRequired และจัดการการแก้ไข MFA ด้วยตนเอง
  3. การอัปเกรดผู้ใช้ที่ไม่ระบุชื่อ: จัดการการลิงก์บัญชีที่ไม่ระบุชื่อหากเปิดใช้ shouldAutoUpgradeAnonymousUsers
  4. สถานะการนำทาง: จัดการการนำทางระหว่างหน้าจอการตรวจสอบสิทธิ์ต่างๆ (การยืนยันหมายเลขโทรศัพท์ การกู้คืนรหัสผ่าน ฯลฯ)
  5. สถานะการโหลด: แสดงตัวบ่งชี้การโหลดระหว่างการดำเนินการตรวจสอบสิทธิ์แบบไม่พร้อมกันโดยการสังเกต authService.authenticationState
  6. การตรวจสอบสิทธิ์อีกครั้ง: จัดการข้อผิดพลาดในการตรวจสอบสิทธิ์อีกครั้งสำหรับการดำเนินการที่มีความละเอียดอ่อน (ดูการตรวจสอบสิทธิ์อีกครั้งในมุมมองที่กำหนดเองด้านล่าง)

การตรวจสอบสิทธิ์อีกครั้งในมุมมองที่กำหนดเอง

เมื่อสร้างมุมมองที่กำหนดเอง ให้จัดการการตรวจสอบสิทธิ์อีกครั้งโดยการตรวจหาข้อผิดพลาดที่เฉพาะเจาะจงและใช้โฟลว์ของคุณเอง การดำเนินการที่ละเอียดอ่อนจะทำให้เกิดข้อผิดพลาดในการตรวจสอบสิทธิ์อีกครั้ง 4 ประเภท ซึ่งแต่ละประเภทจะมีข้อมูลบริบท

รูปแบบการติดตั้งใช้งาน

ผู้ให้บริการ OAuth (Google, Apple, Facebook, Twitter ฯลฯ):

ตรวจหาข้อผิดพลาดและเรียกใช้ reauthenticate(context:) ซึ่งจะจัดการโฟลว์ OAuth โดยอัตโนมัติ

do {
  try await authService.deleteUser()
} catch let error as AuthServiceError {
  if case .oauthReauthenticationRequired(let context) = error {
    try await authService.reauthenticate(context: context)
    try await authService.deleteUser() // Retry operation
  }
}

อีเมล/รหัสผ่าน:

ตรวจหาข้อผิดพลาด แจ้งให้ป้อนรหัสผ่าน สร้างข้อมูลเข้าสู่ระบบ และเรียกใช้ reauthenticate(with:)

do {
  try await authService.updatePassword(to: newPassword)
} catch let error as AuthServiceError {
  if case .emailReauthenticationRequired(let context) = error {
    // Show your password prompt UI
    let password = await promptUserForPassword()
    let credential = EmailAuthProvider.credential(
      withEmail: context.email,
      password: password
    )
    try await authService.reauthenticate(with: credential)
    try await authService.updatePassword(to: newPassword) // Retry
  }
}

โทรศัพท์:

ตรวจหาข้อผิดพลาด ยืนยันโทรศัพท์ สร้างข้อมูลเข้าสู่ระบบ และเรียกใช้ reauthenticate(with:)

do {
  try await authService.deleteUser()
} catch let error as AuthServiceError {
  if case .phoneReauthenticationRequired(let context) = error {
    // Send verification code
    let verificationId = try await authService.verifyPhoneNumber(
      phoneNumber: context.phoneNumber
    )
    // Show your SMS code input UI
    let code = await promptUserForSMSCode()
    let credential = PhoneAuthProvider.provider().credential(
      withVerificationID: verificationId,
      verificationCode: code
    )
    try await authService.reauthenticate(with: credential)
    try await authService.deleteUser() // Retry
  }
}

ลิงก์อีเมล:

ตรวจหาข้อผิดพลาด ส่งอีเมลยืนยัน และจัดการ URL ขาเข้า

do {
  try await authService.updatePassword(to: newPassword)
} catch let error as AuthServiceError {
  if case .emailLinkReauthenticationRequired(let context) = error {
    // Send verification email
    try await authService.sendEmailSignInLink(
      email: context.email,
      isReauth: true
    )
    // Show your "Check your email" UI
    await showCheckEmailUI()
    // When user taps the link, it opens your app with a URL
    // Handle it in your URL handler:
    // try await authService.handleSignInLink(url: url)
    // The handleSignInLink method automatically completes reauthentication
    try await authService.updatePassword(to: newPassword) // Retry
  }
}

ออบเจ็กต์บริบทการตรวจสอบสิทธิ์อีกครั้งทั้งหมดมีพร็อพเพอร์ตี้ .displayMessage สำหรับข้อความที่แสดงต่อผู้ใช้

ผู้ให้บริการ OAuth ที่กำหนดเอง

คุณสร้างผู้ให้บริการ OAuth ที่กำหนดเองสำหรับบริการอื่นๆ นอกเหนือจากบริการที่มีอยู่ได้โดยทำดังนี้

⚠️ สำคัญ: คุณต้องกำหนดค่าผู้ให้บริการ OIDC (OpenID Connect) ในการตั้งค่าการตรวจสอบสิทธิ์ของโปรเจ็กต์ Firebase ก่อนจึงจะใช้งานได้ ในคอนโซล Firebase ให้ไปที่การตรวจสอบสิทธิ์ → วิธีการลงชื่อเข้าใช้ แล้วเพิ่มผู้ให้บริการ OIDC พร้อมข้อมูลเข้าสู่ระบบที่จำเป็น (รหัสไคลเอ็นต์, ข้อมูลลับไคลเอ็นต์, URL ผู้ออก) นอกจากนี้ คุณต้องลงทะเบียน URI การเปลี่ยนเส้นทาง OAuth ที่ Firebase ระบุไว้ในคอนโซลนักพัฒนาแอปของผู้ให้บริการด้วย ดูวิธีการตั้งค่าแบบละเอียดได้ที่เอกสารประกอบ OIDC ของ Firebase

import FirebaseAuthSwiftUI
import FirebaseOAuthSwiftUI
import SwiftUI

struct ContentView: View {
  let authService: AuthService

  init() {
    let configuration = AuthConfiguration()
    
    authService = AuthService(configuration: configuration)
      .withOAuthSignIn(
        OAuthProviderSwift(
          providerId: "oidc.line",  // LINE OIDC provider
          scopes: ["profile", "openid", "email"],  // LINE requires these scopes
          displayName: "Sign in with LINE",
          buttonIcon: Image("line-logo"),
          buttonBackgroundColor: .green,
          buttonForegroundColor: .white
        )
      )
      .withOAuthSignIn(
        OAuthProviderSwift(
          providerId: "oidc.custom-provider",
          scopes: ["profile", "openid"],
          displayName: "Sign in with Custom",
          buttonIcon: Image(systemName: "person.circle"),
          buttonBackgroundColor: .purple,
          buttonForegroundColor: .white
        )
      )
  }

  var body: some View {
    AuthPickerView {
      Text("App Content")
    }
    .environment(authService)
  }
}