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

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

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

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

قبل از شروع

نصب

‫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 (ورود به سیستم با X)
    • FirebaseOAuthSwiftUI (ارائه‌دهندگان استاندارد OAuth و OIDC مانند GitHub، ‏Microsoft، ‏Yahoo)
  5. برای نصب کتابخانه‌های انتخاب‌شده، روی افزودن بسته کلیک کنید.

  6. AuthService را در مقداردهی اولیه سطح بالای خود پیکربندی کنید 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 را وقتی نشانی وب بازشده پیوند ورود به سیستم 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» برای وب توضیح داده شده است با برنامه‌تان مرتبط کنید. وقتی پیام‌واره نشان داده شد، نشانی وب زیر را به‌عنوان «نشانی وب برگشتی» ثبت کنید:
      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. طرح‌های نشانی وب سفارشی را به پروژه Xcode خود اضافه کنید:

    1. پیکربندی پروژه را باز کنید: روی نام پروژه در نمای درختی سمت راست کلیک کنید. برنامه خود را از بخش هدف‌ها انتخاب کنید، سپس برگه اطلاعات را انتخاب کنید و بخش انواع نشانی وب را ازهم باز کنید.

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

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

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

    let authService = AuthService()
      .withGoogleSignIn()
    

فیس‌بوک

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

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

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

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

    let authService = AuthService()
     .withFacebookSignIn()
    

شماره تلفن

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

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

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

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

    1. پیکربندی پروژه را باز کنید: روی نام پروژه در نمای درختی سمت راست کلیک کنید. برنامه خود را از بخش هدف‌ها انتخاب کنید، سپس برگه اطلاعات را انتخاب کنید و بخش انواع نشانی وب را ازهم باز کنید.

    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)

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

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

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

  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» پیکربندی کنید، سپس 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. ارائه برگ: میانای کاربری اصالت‌سنجی به‌صورت برگ مُدال ظاهر می‌شود
  2. پیمایش داخلی: پیمایش خودکار بین صفحه‌های ورود به سیستم، بازیابی گذرواژه، «احراز هویت چندعاملی»، پیوند ایمیل، و درستی‌سنجی تلفن
  3. مدیریت وضعیت اصالت‌سنجی: براساس authService.authenticationState، به‌طور خودکار بین میانای کاربر اصالت‌سنجی و محتوای شما جابه‌جا می‌شود
  4. کنترل ازطریق isPresented: با تنظیم authService.isPresented = true/false، کنترل کنید برگه مجوز چه زمانی ظاهر شود

رفتارهای نظرپردازانه

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

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

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

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

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

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

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

  • هنگام ورود به سیستم، به‌طور خودکار تشخیص می‌دهد که آیا به «احراز هویت چندعاملی» نیاز است یا نه
  • صفحه‌های مناسب برای حل مشکل «احراز هویت چندعاملی» (پیامک یا TOTP) را ارائه می‌دهد
  • جریان‌های ثبت‌نام و مدیریت «احراز هویت چندعاملی» را مدیریت می‌کند
  • از عوامل گذرواژه یکبارمصرف مبتنی بر پیامک و مبتنی بر زمان (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. مدیریت MFA: SignInOutcome را برای .mfaRequired بررسی کنید و حل MFA را به‌صورت دستی انجام دهید
  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
  }
}

پیوند ایمیل:

خطا را دریافت کنید، ایمیل درستی‌سنجی ارسال کنید، و 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) باید در تنظیمات «احراز هویت» پروژه Firebase شما پیکربندی شوند تا بتوان از آن‌ها استفاده کرد. در «کنسول Firebase»، به اصالت‌سنجی → روش ورود به سیستم بروید و ارائه‌دهنده OIDC خود را با اعتبارنامه‌های موردنیاز (شناسه کارخواه، رمز کارخواه، نشانی وب صادرکننده) اضافه کنید. همچنین باید نشانی وب هدایت 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)
  }
}