افزودن ورود به سیستم به برنامه iOS با FirebaseUI

FirebaseUI برای SwiftUI یک کتابخانه مدرن و SwiftUI-first است که برپایه Firebase Authentication ساخته شده است و جریان‌های ورود ازپیش‌ساخته‌شده را برای برنامه شما ارائه می‌دهد.

FirebaseUI برای SwiftUI مزایای زیر را دارد:

  • میانای کاربر پیش‌فرض با طراحی خاص: افزودن جریان ورود به سیستم کامل با AuthPickerView.
  • قابل‌سفارشی‌سازی: دکمه‌های پیش‌فرض را در چیدمان خودتان ارائه دهید یا تجربه‌ای کاملاً سفارشی بسازید.
  • پیوند دادن حساب ناشناس: به‌طور اختیاری کاربران ناشناس را به‌جای جایگزین کردن ارتقا دهید.
  • مدیریت حساب: جریان‌های داخلی برای ثبت‌نام، بازیابی گذرواژه، و مدیریت حساب.
  • ارائه‌دهندگان متعدد: ایمیل/گذرواژه، پیوند ایمیل، اصالت‌سنجی تلفن، Apple،‏ Google،‏ Facebook،‏ Twitter، و ارائه‌دهندگان استاندارد OAuth2/OIDC.
  • ویژگی‌های اصالت‌سنجی مدرن: پشتیبانی داخلی از اصالت‌سنجی چندعاملی (MFA) و میاناهای برنامه‌سازی کاربردی async/await.

قبل از شروع

نصب

FirebaseUI برای SwiftUI به‌عنوان بسته Swift ارائه می‌شود. برای نصب بسته در پروژه Xcode، کارهای زیر را انجام دهید:

  1. در Xcode، روی File > Add Package Dependencies (فایل > افزودن وابستگی‌های بسته) کلیک کنید.

  2. نشانی وب بسته را وارد کنید:

    https://github.com/firebase/FirebaseUI-iOS
    
  3. در منو قانون وابستگی، تا نسخه عمده بعدی را انتخاب کنید و حداقل نسخه را روی جدیدترین نسخه پخش تنظیم کنید.

  4. روی افزودن بسته کلیک کنید، سپس کتابخانه‌هایی را که می‌خواهید به پروژه‌تان اضافه شود انتخاب کنید.

    کتابخانه زیر همیشه لازم است. این کیت شامل وابستگی‌های اصلی و همچنین پشتیبانی از ورود به سیستم با ایمیل-گذرواژه و ورود به سیستم با پیوند ایمیل است.

    • FirebaseAuthSwiftUI

     اگر می‌خواهید از روش‌های ورود به سیستم دیگر پشتیبانی کنید، یک یا چند مورد از کتابخانه‌های زیر را نیز انتخاب کنید:

    • 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:) اضافه کنید که وقتی نشانی وب بازشده پیوند ورود به سیستم Firebase Authentication است، true را برگرداند. این منطق نشان می‌دهد که پیوند توسط 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» برای وب شرح داده شده است، وب‌سایتتان را با برنامه‌تان مرتبط کنید. وقتی درخواست شد، نشانی وب زیر را به‌عنوان «نشانی وب بازگشت» ثبت کنید:
      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 Team و کلید خصوصی و شناسه کلیدی را که در بخش قبلی ایجاد کردید مشخص کنید.
  3. در Xcode، بخش Signing & Capabilities (امضا و قابلیت‌ها) را در ویرایشگر پروژه باز کنید و قابلیت Sign in with Apple (ورود به سیستم با Apple) را اضافه کنید.

  4. ارائه‌دهنده را در نمونه AuthService خود ثبت کنید:

    let authService = AuthService()
      .withAppleSignIn()
    

Google

برای استفاده از «ورود به سیستم Google»:

  1. از بخش امنیت > اصالت‌سنجی > روش ورود به سیستم در کنسول Firebase، ارائه‌دهنده Google را فعال کنید.

  2. نسخه جدیدی از فایل GoogleService-Info.plist پروژه‌تان را بارگیری کنید و آن را در پروژه Xcode خود کپی کنید. نسخه جدید را جایگزین نسخه‌های موجود کنید.

  3. طرح‌واره‌های نشانی وب سفارشی را به پروژه Xcode خود اضافه کنید:

    1. پیکربندی پروژه را باز کنید: روی نام پروژه در نمای درختی سمت راست کلیک کنید. برنامه‌تان را از بخش TARGETS انتخاب کنید، سپس برگه Info را انتخاب کنید و بخش URL Types را ازهم باز کنید.

    2. روی دکمه + کلیک کنید و طرح‌واره نشانی وب را برای شناسه مشتری معکوس‌شده‌تان اضافه کنید. برای پیدا کردن این مقدار، فایل پیکربندی GoogleService-Info.plist را باز کنید و به‌دنبال کلید REVERSED_CLIENT_ID بگردید. مقدار آن کلید را کپی کنید، و آن را در چارگوش طرح‌واره‌های نشانی وب در صفحه پیکربندی جای‌گذاری کنید. بقیه فیلدها را بدون تغییر باقی بگذارید.

      پس‌از تکمیل، پیکربندی شما باید چیزی شبیه به موارد زیر باشد (اما با مقادیر مختص برنامه‌تان):

  4. ارائه‌دهنده را در نمونه AuthService خود ثبت کنید:

    let authService = AuthService()
      .withGoogleSignIn()
    

فیس‌بوک

برای استفاده از «ورود به سیستم Facebook»:

  1. با دنبال کردن دستورالعمل‌های سایت Meta for Developers، «ورود به سیستم Facebook» را برای کیت توسعه نرم‌افزار iOS راه‌اندازی کنید. مرحله نهایی، «افزودن ورود به سیستم Facebook به کد شما» را رد کنید.

  2. از بخش امنیت > اصالت‌سنجی > روش ورود به سیستم در کنسول Firebase، ارائه‌دهنده Facebook را فعال کنید. به «شناسه برنامه Facebook» و «رمز برنامه» از سایت Meta for Developers نیاز خواهید داشت.

  3. ارائه‌دهنده را در نمونه AuthService خود ثبت کنید:

    let authService = AuthService()
     .withFacebookSignIn()
    

شماره تلفن

برای استفاده از اصالت‌سنجی تلفنی:

  1. از بخش امنیت > اصالت‌سنجی > روش ورود به سیستم کنسول Firebase، ارائه‌دهنده تلفن را فعال کنید.

  2. APN را برای برنامه‌تان طبق دستورالعمل‌های بخش شروع دریافت اعلان‌های بی‌صدا پیکربندی کنید.

  3. طرح‌واره‌های نشانی وب سفارشی را به پروژه Xcode خود اضافه کنید:

    1. پیکربندی پروژه را باز کنید: روی نام پروژه در نمای درختی سمت راست کلیک کنید. برنامه‌تان را از بخش TARGETS انتخاب کنید، سپس برگه Info را انتخاب کنید و بخش URL Types را ازهم باز کنید.

    2. روی دکمه + کلیک کنید و «شناسه برنامه کدگذاری‌شده» خود را به‌عنوان طرح‌واره نشانی وب اضافه کنید. برای پیدا کردن این مقدار، تنظیمات > موارد کلی را در کنسول 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()
    

توییتر (X)

برای استفاده از «ورود به سیستم با Twitter»:

  1. با دنبال کردن دستورالعمل‌های سایت «توسعه‌دهندگان X»، اطلاعات کاربری میانای برنامه‌سازی کاربردی X را تولید کنید.

  2. از بخش امنیت > اصالت‌سنجی > روش ورود به سیستم در کنسول Firebase، ارائه‌دهنده Twitter را فعال کنید. به کلید میانای برنامه‌سازی کاربردی و رمز میانای برنامه‌سازی کاربردی از کنسول توسعه‌دهنده X نیاز خواهید داشت.

  3. ارائه‌دهنده را در نمونه AuthService خود ثبت کنید:

    let authService = AuthService()
      .withTwitterSignIn()
    

ارائه‌دهندگان استاندارد OAuth2 و OIDC

‫FirebaseUI همچنین از ارائه‌دهندگان OAuth داخلی مانند GitHub،‏ Microsoft، و Yahoo، و همچنین ارائه‌دهندگان OIDC سفارشی پیکربندی‌شده در Firebase Authentication پشتیبانی می‌کند.

 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 را ارائه می‌دهد، یک واسط کاربر اصالت‌سنجی ازپیش‌ساخته و با نظر شخصی که کل جریان اصالت‌سنجی را برای شما مدیریت می‌کند. این ساده‌ترین راه برای افزودن اصالت‌سنجی به برنامه‌تان است.

مثال

در اینجا مثالی از 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: از رشته‌های سفارشی از «دسته» مشخص‌شده استفاده کنید. این مورد برای بومی‌سازی و همچنین برای سفارشی‌سازی رشته‌های پیش‌فرض استفاده‌شده توسط AuthPickerView استفاده می‌شود. برای مثال، برای تنظیم پیام نمایش‌داده‌شده در بالای برگه اصالت‌سنجی، Localizable.strings را با این محتوا ایجاد کنید:

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

آنچه در نمای ازپیش‌ساخته گنجانده شده است

وقتی از AuthPickerView استفاده می‌کنید، این موارد را دریافت می‌کنید:

  1. Sheet Presentation: Authentication UI appears as a modal sheet
  2. پیمایش داخلی: پیمایش خودکار بین صفحه‌های ورود به سیستم، بازیابی گذرواژه، MFA، پیوند ایمیل، و درستی‌سنجی تلفنی
  3. مدیریت وضعیت اصالت‌سنجی: به‌طور خودکار بین میانای کاربر اصالت‌سنجی و محتوای شما براساس authService.authenticationState جابه‌جا می‌شود
  4. کنترل ازطریق isPresented: با تنظیم authService.isPresented = true/false، زمان نمایش برگه اصالت‌سنجی را کنترل کنید

رفتارهای متعصبانه

AuthPickerView پیش‌فرض درباره نحوه مدیریت چندین سناریو پیچیده نظر می‌دهد:

۱. حل تعارض حساب

وقتی تداخلی در حساب رخ می‌دهد (مثلاً ورود به سیستم با اطلاعات اعتباری که قبلاً به حساب دیگری پیوند شده است)، AuthPickerView به‌طور خودکار آن را مدیریت می‌کند:

  • تداخل‌های ارتقای ناشناس: اگر shouldAutoUpgradeAnonymousUsers فعال باشد و تداخلی درطول ارتقای ناشناس رخ دهد، سیستم به‌طور خودکار کاربر ناشناس را از سیستم خارج می‌کند و با اطلاعات کاربری جدید به سیستم وارد می‌شود.
  • سایر تداخل‌ها: برای تداخل‌های اطلاعات کاربری بین حساب‌های غیرناشناس، سیستم اطلاعات کاربری معلق را ذخیره می‌کند و پس‌از ورود موفق به سیستم، تلاش می‌کند آن را پیوند دهد.

این مورد توسط AccountConflictModifier اعمال‌شده در سطح NavigationStack مدیریت می‌شود.

۲. اصالت‌سنجی چندعاملی (MFA)

وقتی «احراز هویت چندعاملی» در پیکربندی شما فعال باشد:

  • به‌طور خودکار تشخیص می‌دهد که چه زمانی درطول ورود به سیستم به MFA نیاز است
  • صفحه‌نمایش‌های مناسب برای حل مشکل اصالت‌سنجی چندعاملی (پیامک یا گذرواژه یک‌بارمصرف زمان‌مبنا) را ارائه می‌دهد
  • جریان‌های ثبت‌نام و مدیریت اصالت‌سنجی چندعاملی را مدیریت می‌کند
  • از هر دو عامل «گذرواژه یک‌بارمصرف» (TOTP) مبتنی بر پیامک و زمان‌مبنا پشتیبانی می‌کند
۳. مدیریت کردن خطا

نماهای پیش‌فرض شامل مدیریت خطای داخلی است:

  • پیام‌های خطای کاربرپسند را در کادرهای گفتگوی هشدار نمایش می‌دهد
  • خطاهایی را که به‌طور داخلی رسیدگی می‌شوند (مثلاً خطاهای لغو، تداخل‌های رسیدگی‌شده به‌طور خودکار) به‌طور خودکار فیلتر می‌کند
  • از پیام‌های خطای بومی‌سازی‌شده ازطریق StringUtils استفاده می‌کند
  • خطاها ازطریق کلید محیط reportError منتشر می‌شوند

وقتی ورود به سیستم با پیوند ایمیل پیکربندی شده باشد:

  • نشانی ایمیل را به‌طور خودکار در فضای ذخیره‌سازی برنامه ذخیره می‌کند
  • پیمایش پیوند عمیق از ایمیل را مدیریت می‌کند
  • جریان کامل درستی‌سنجی ایمیل را مدیریت می‌کند
  • از ارتقای کاربر ناشناس ازطریق پیوند ایمیل پشتیبانی می‌کند
۵. ارتقای خودکار کاربر ناشناس

وقتی shouldAutoUpgradeAnonymousUsers فعال باشد:

  • به‌طور خودکار تلاش می‌کند حساب‌های ناشناس را با اعتبارنامه‌های جدید ورود به سیستم پیوند دهد
  • با ارتقا دادن به‌جای جایگزین کردن جلسه‌های ناشناس، داده‌های کاربر را حفظ می‌کند
  • تداخل‌های ارتقا را به‌خوبی مدیریت می‌کند
‫۶. اصالت‌سنجی مجدد در نماهای پیش‌فرض

عملیات حساس مانند حذف حساب‌ها، به‌روزرسانی گذرواژه‌ها، یا لغو ثبت‌نام عوامل اصالت‌سنجی چندعاملی به اصالت‌سنجی اخیر نیاز دارند. هنگام استفاده از نماهای پیش‌فرض، اصالت‌سنجی مجدد به‌طور خودکار براساس ارائه‌دهنده ورود به سیستم کاربر انجام می‌شود.

وقتی عملیات حساسی به اصالت‌سنجی مجدد نیاز داشته باشد، نماهای پیش‌فرض به‌طور خودکار:

  • ارائه‌دهندگان OAuth (‏Google،‏ Apple،‏ Facebook،‏ Twitter، و غیره): هشداری نمایش دهید که از کاربر می‌خواهد تأیید کند، سپس به‌طور خودکار اطلاعات کاربری جدید را دریافت کنید و عملیات را تکمیل کنید.

  • ایمیل/گذرواژه: برگه‌ای را ارائه دهید که از کاربر می‌خواهد قبل‌از ادامه دادن، گذرواژه‌اش را وارد کند.

  • پیوند ایمیل: هشداری نشان دهید که درخواست ارسال ایمیل درستی‌سنجی می‌کند، سپس برگه‌ای با دستورالعمل‌های بررسی ایمیل ارائه دهید. کاربر روی پیوند موجود در ایمیل خود ضربه می‌زند تا بازدرستی‌سنجی را تکمیل کند.

  • تلفن: هشداری نشان دهید که توضیح می‌دهد درستی‌سنجی لازم است، سپس برگه‌ای برای درستی‌سنجی کد پیامکی ارائه دهید.

پس‌از بازاصالت‌سنجی موفق، عملیات به‌طور خودکار دوباره امتحان می‌شود. هنگام استفاده از AuthPickerView یا نماهای مدیریت حساب داخلی (UpdatePasswordView، SignedInView، و غیره) به کد اضافی نیاز نیست.

پیشرفته: ساختن نماهای اصالت‌سنجی سفارشی

اگر به کنترل بیشتری روی واسط کاربر یا جریان پیمایش نیاز دارید، می‌توانید نماهای اصالت‌سنجی سفارشی خودتان را بسازید و همچنان از AuthService برای منطق اصالت‌سنجی استفاده کنید.

می‌توانید عناصر FirebaseUI را به روش‌های مختلفی با منطق سفارشی ترکیب کنید. بخش‌های زیر شامل نمونه‌هایی از برخی رویکردهای سفارشی‌سازی است که ممکن است استفاده کنید.

رویکرد ۱: دکمه‌های سفارشی با registerProvider()

برای کنترل کامل بر ظاهر دکمه، می‌توانید پیاده‌سازی سفارشی AuthProviderUI خودتان را ایجاد کنید که هر ارائه‌دهنده‌ای را دربرمی‌گیرد و نمای دکمه سفارشی شما را برمی‌گرداند.

ایجاد واسط کاربر ارائه‌دهنده سفارشی

در اینجا نحوه ایجاد دکمه سفارشی 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،‏ Phone، و ارائه‌دهندگان OAuth. به‌سادگی نمای دکمه سفارشی‌تان را ایجاد کنید و آن را در کلاسی مطابق با AuthProviderUI قرار دهید.

رویکرد ۲: دکمه‌های پیش‌فرض با نماهای سفارشی

می‌توانید از 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)
  }
}

رویکرد ۳: نماهای سفارشی با ناوبری سفارشی

برای کنترل کامل بر کل جریان، می‌توانید 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. مدیریت اصالت‌سنجی چندعاملی: SignInOutcome را برای .mfaRequired بررسی کنید و وضوح اصالت‌سنجی چندعاملی را به‌صورت دستی مدیریت کنید
  3. ارتقاهای کاربر ناشناس: اگر shouldAutoUpgradeAnonymousUsers فعال باشد، پیوند دادن حساب‌های ناشناس را مدیریت کنید
  4. وضعیت پیمایش: مدیریت پیمایش بین صفحه‌های اصالت‌سنجی مختلف (درستی‌سنجی تلفنی، بازیابی گذرواژه، و غیره)
  5. حالت‌های بار کردن: با مشاهده authService.authenticationState، نشانگرهای بار کردن را درطول عملیات اصالت‌سنجی ناهمگام نشان دهید
  6. اصالت‌سنجی مجدد: رسیدگی به خطاهای اصالت‌سنجی مجدد برای عملیات حساس (به بخش اصالت‌سنجی مجدد در نماهای سفارشی در زیر مراجعه کنید)

اصالت‌سنجی مجدد در نماهای سفارشی

هنگام ساختن نماهای سفارشی، با گرفتن خطاهای خاص و پیاده‌سازی جریان خودتان، اصالت‌سنجی مجدد را مدیریت کنید. عملیات حساس چهار نوع خطای بازدرستی‌سنجی ایجاد می‌کند که هرکدام حاوی اطلاعات زمینه‌ای است.

الگوهای پیاده‌سازی

ارائه‌دهندگان 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
  }
}

پیوند ایمیل:

خطا را بگیرید، ایمیل درستی‌سنجی ارسال کنید، و نشانی وب ورودی را مدیریت کنید:

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 خود را با اعتبارنامه‌های لازم (شناسه کارخواه، رمز کارخواه، نشانی وب صادرکننده) اضافه کنید. همچنین باید URI هدایت OAuth ارائه‌شده توسط Firebase را در کنسول توسعه‌دهنده ارائه‌دهنده خود ثبت کنید. برای دستورالعمل‌های راه‌اندازی دقیق، به اسناد Firebase OIDC مراجعه کنید.

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