Добавьте функцию «Войти через Google» в приложение iOS

1. Прежде чем начать

В этом практическом занятии вы научитесь создавать iOS-приложение, реализующее вход через Google и работающее в симуляторе. Предоставляются реализации с использованием SwiftUI и UIKit.

SwiftUI — это современный фреймворк Apple для разработки пользовательских интерфейсов новых приложений. Он позволяет создавать пользовательские интерфейсы для всех платформ Apple из единой общей кодовой базы. Для его работы требуется минимальная версия iOS 13.

UIKit — это оригинальный и основополагающий фреймворк пользовательского интерфейса от Apple для iOS. Он обеспечивает обратную совместимость со старыми версиями iOS. Это делает его хорошим выбором для уже существующих приложений, которым необходимо поддерживать различные старые устройства.

Вы можете выбрать тот фреймворк, который наилучшим образом соответствует вашим потребностям в разработке.

Предварительные требования

Что вы узнаете

  • Как создать проект в Google Cloud
  • Как создать OAuth-клиенты в консоли Google Cloud
  • Как реализовать вход через Google для вашего iOS-приложения
  • Как настроить кнопку «Войти через Google»
  • Как расшифровать идентификационный токен
  • Как включить проверку приложений (App Check) для вашего iOS-приложения

Что вам понадобится

Данный практический пример был создан с использованием Xcode 16.3 и симулятора iOS 18.3. Для разработки следует использовать последнюю версию Xcode.

2. Создайте новый проект Xcode.

  1. Откройте Xcode и выберите «Создать новый проект Xcode» .
  2. Выберите вкладку iOS , выберите шаблон приложения и нажмите «Далее» .

Страница шаблона создания проекта Xcode

  1. В параметрах проекта:
    • Введите название вашего продукта .
    • При желании выберите свою команду .
    • Введите идентификатор вашей организации .
    • Запишите сгенерированный идентификатор пакета . Он понадобится вам позже.
    • Для интерфейса выберите один из следующих вариантов:
      • SwiftUI для приложения на основе SwiftUI.
      • Раскадровка для приложения на основе UIKit.
    • Выберите Swift в качестве языка программирования .
    • Нажмите «Далее» и выберите место для сохранения проекта.

страница параметров проекта Xcode

3. Создайте OAuth-клиент

Для взаимодействия вашего приложения со службами аутентификации Google необходимо создать идентификатор клиента OAuth. Для этого требуется проект Google Cloud. Следующие шаги помогут вам создать проект и идентификатор клиента OAuth.

Выберите или создайте проект Google Cloud.

  1. Перейдите в консоль Google Cloud и выберите или создайте проект. Если вы выбираете уже существующий проект, консоль автоматически перенаправит вас к следующему необходимому шагу.

Страница выбора проекта в Google Cloud Console

  1. Введите название для вашего нового проекта в Google Cloud.
  2. Выберите «Создать» .

Страница выбора проекта в Google Cloud Console

Если вы уже настроили экран согласия для выбранного проекта, вам не будет предложено настроить его сейчас. В этом случае вы можете пропустить этот раздел и перейти к созданию клиента OAuth 2.0 .

  1. Выберите экран «Настроить согласие» .

Создание страницы клиента OAuth в Google Cloud Console с настройкой экрана согласия.

  1. На странице «Брендинг» выберите «Начать» .

Страница «Начало работы» в Google Cloud Console

  1. На странице конфигурации проекта заполните следующие поля:
    • Информация о приложении : Введите название и адрес электронной почты службы поддержки пользователей для вашего приложения. Этот адрес электронной почты службы поддержки будет отображаться публично, чтобы пользователи могли связаться с вами с вопросами, касающимися их согласия.
    • Целевая аудитория : выберите «Внешняя» .
    • Контактная информация : Введите адрес электронной почты, чтобы Google мог связаться с вами по поводу вашего проекта.
    • Ознакомьтесь с политикой Google API Services: User Data Policy.
    • Нажмите «Создать» .

Страница настройки брендинга клиента Google Cloud Console

  1. В навигационном меню выберите страницу «Клиенты» .
  2. Нажмите «Создать клиента» .

Страница клиентов проекта Google Cloud

Создайте клиент OAuth 2.0.

  1. В качестве типа приложения выберите iOS .
  2. Введите имя для вашего клиента.
  3. Введите идентификатор пакета, созданный на предыдущем шаге.
  4. Введите идентификатор команды (Team ID), присвоенный вашей команде компанией Apple. Этот шаг пока необязателен, но идентификатор команды необходим для включения функции App Check в дальнейшем в этом практическом задании.
  5. Выберите «Создать» .

Страница ввода данных клиента OAuth

  1. Скопируйте идентификатор клиента из диалогового окна, он понадобится вам позже.
  2. Загрузите файл plist для дальнейшего использования в качестве справочного материала.

Диалог создания идентификатора клиента OAuth

4. Настройте свой проект Xcode.

Следующий шаг — настройка проекта Xcode для работы с SDK «Вход через Google». Этот процесс включает добавление SDK в проект в качестве зависимости и настройку параметров проекта с уникальным идентификатором клиента. Этот идентификатор позволяет SDK безопасно взаимодействовать со службой аутентификации Google во время процесса входа в систему.

Установите зависимости "Вход через Google".

  1. Откройте свой проект Xcode.
  2. Перейдите в меню Файл > Добавить зависимости пакета .
  3. В строке поиска введите URL-адрес репозитория «Вход через Google»: https://github.com/google/GoogleSignIn-iOS

Найдите зависимость "Вход через Google" в менеджере пакетов Swift.

  1. Выберите «Добавить пакет» .
  2. Выберите основное целевое приложение для пакета GoogleSignIn .
  3. Если вы используете SwiftUI, выберите основной целевой объект приложения для пакета GoogleSignInSwift . Если вы планируете использовать UIKit, не выбирайте целевой объект для этого пакета.
  4. Выберите «Добавить пакет» .

Добавьте зависимость "Вход через Google" в свой проект.

Настройте учетные данные вашего приложения.

  1. В навигаторе проектов щелкните по корневому каталогу вашего проекта.
  2. В основной области редактора выберите основной целевой объект вашего приложения из списка TARGETS .
  3. Выберите вкладку «Информация» в верхней части области редактора.
  4. Наведите курсор на последнюю строку в разделе «Пользовательские свойства целевого объекта iOS» и нажмите появившуюся кнопку «+» .

Добавьте новый ключ цели в свойства цели iOS.

  1. В столбце «Ключ» введите GIDClientID
  2. В столбце «Значение» вставьте идентификатор клиента, скопированный из консоли Google Cloud.

Добавьте GIDClientID в качестве основного целевого объекта приложения.

  1. Откройте файл plist , который вы скачали из консоли Google Cloud.
  2. Скопируйте значение для поля "Обратный идентификатор клиента" .

файл plist консоли Google Cloud

  1. Разверните раздел «Типы URL» в нижней части вкладки «Информация» .
  2. Нажмите кнопку «+» .
  3. Введите обратный идентификатор клиента в поле «Схемы URL» .

Добавьте ключ URLSchemes к основному целевому объекту приложения.

Теперь мы готовы начать добавлять кнопку входа в наше приложение!

5. Добавьте кнопку входа в систему.

После настройки проекта Xcode пришло время добавить кнопку «Войти через Google» в приложение!

Основная логика этого шага заключается в вызове метода GIDSignIn.sharedInstance.signIn . Этот метод запускает процесс аутентификации, передавая управление SDK Sign in with Google для отображения пользователю процесса авторизации через Google.

SwiftUI

  1. Найдите файл ContentView.swift в навигаторе проектов Xcode.
  2. Замените содержимое этого файла следующим текстом:
import GoogleSignIn
import GoogleSignInSwift
import SwiftUI

struct ContentView: View {
  var body: some View {
    VStack {
      GoogleSignInButton(action: handleSignInButton).padding()
    }
  }

  func handleSignInButton() {
    // Find the current window scene.
    guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene else {
      print("There is no active window scene")
      return
    }

    // Get the root view controller from the window scene.
    guard
      let rootViewController = windowScene.windows.first(where: { $0.isKeyWindow })?
        .rootViewController
    else {
      print("There is no key window or root view controller")
      return
    }

    // Start the sign-in process.
    GIDSignIn.sharedInstance.signIn(
      withPresenting: rootViewController
    ) { signInResult, error in
      guard let result = signInResult else {
        // Inspect error
        print("Error signing in: \(error?.localizedDescription ?? "No error description")")
        return
      }
      // If sign in succeeded, display the app's main content View.
      print("ID Token: \(result.user.idToken?.tokenString ?? "")")
    }
  }
}

#Preview {
  ContentView()
}

Кнопка «Войти через Google» в симуляторе iOS на основе фреймворка SwiftUI

UIKit

  1. Найдите файл ViewController.swift в навигаторе проектов Xcode.
  2. Замените содержимое этого файла следующим текстом:
import GoogleSignIn
import UIKit

class ViewController: UIViewController {

  // Create an instance of the Sign in with Google button
  let signInButton = GIDSignInButton()

  override func viewDidLoad() {
    super.viewDidLoad()

    // Add the sign-in button to your view
    view.addSubview(signInButton)

    // Position the button using constraints
    signInButton.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
      signInButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
      signInButton.centerYAnchor.constraint(equalTo: view.centerYAnchor),
    ])

    // Add a target to the button to call a method when it's pressed
    signInButton.addTarget(self, action: #selector(signInButtonTapped), for: .touchUpInside)
  }

  // This method is called when the sign-in button is pressed.
  @objc func signInButtonTapped() {
    // Start the sign-in process.
    GIDSignIn.sharedInstance.signIn(withPresenting: self) { signInResult, error in
      guard let result = signInResult else {
        // Inspect error
        print("Error signing in: \(error?.localizedDescription ?? "No error description")")
        return
      }

      // If sign in succeeded, print the ID token.
      print("ID Token: \(result.user.idToken?.tokenString ?? "")")
    }
  }
}

Фреймворк UIKit. Кнопка «Войти через Google» в симуляторе iOS.

Посмотреть кнопку входа

Запустите приложение в симуляторе. Вы увидите кнопку «Войти через Google», но она пока не будет работать должным образом. Это нормально, поскольку вам еще нужно реализовать код для обработки перенаправления обратно в ваше приложение после аутентификации пользователя.

6. Настройте кнопку входа в систему.

Вы можете настроить стандартную кнопку «Вход через Google», чтобы она лучше соответствовала теме вашего приложения. SDK для входа через Google позволяет изменять цветовую схему и стиль кнопки.

SwiftUI

Кнопка по умолчанию добавляется на страницу с помощью следующей строки кода:

GoogleSignInButton(action: handleSignInButton)

Кнопку GoogleSignInButton можно настроить, передав параметры в её инициализатор. Следующий код отобразит кнопку входа в тёмном режиме.

  1. Откройте файл ContentView.swift
  2. Обновите инициализатор для кнопки GoogleSignInButton , добавив следующие значения:
GoogleSignInButton(
  scheme: .dark,  // Options: .light, .dark, .auto
  style: .standard,  // Options: .standard, .wide, .icon
  state: .normal,  // Options: .normal, .disabled
  action: handleSignInButton
).padding()

Темный режим SwiftUI Framework Кнопка «Войти через Google» в симуляторе iOS

Для получения более подробной информации о параметрах настройки см. Справочник по фреймворку GoogleSignInSwift.

UIKit

Кнопка по умолчанию создается с помощью следующих строк кода:

// Create an instance of the Sign in with Google button
let signInButton = GIDSignInButton()

// Add the button to your view
view.addSubview(signInButton)

Компонент GIDSignInButton настраивается путем установки свойств экземпляра кнопки. Следующий код позволит отобразить кнопку входа в систему в темном режиме.

  1. Откройте файл ViewController.swift .
  2. Добавьте следующие строки кода непосредственно перед добавлением кнопки входа в представление в функции viewDidLoad :
// Set the width and color of the sign-in button
signInButton.style = .standard  // Options: .standard, .wide, .iconOnly
signInButton.colorScheme = .dark  // Options: .dark, .light

Темный режим фреймворка UIKit. Кнопка «Войти через Google» в симуляторе iOS.

Для получения дополнительной информации о настройке см. Справочник по фреймворку GoogleSignIn.

7. Обработка URL-адреса перенаправления аутентификации.

После добавления кнопки входа в систему следующим шагом является обработка перенаправления, которое происходит после аутентификации пользователя. После аутентификации Google возвращает URL-адрес с временным кодом авторизации. Для завершения процесса входа в систему обработчик перехватывает этот URL-адрес и передает его в SDK Sign in with Google для обмена на подписанный токен идентификации (JWT).

SwiftUI

  1. Откройте файл, содержащий структуру вашего App . Имя этого файла определяется вашим проектом, поэтому оно будет выглядеть примерно так: YourProjectNameApp.swift .
  2. Замените содержимое этого файла следующим текстом:
import GoogleSignIn
import SwiftUI

@main
struct iOS_Sign_in_with_Google_App: App {
  var body: some Scene {
    WindowGroup {
      ContentView()

        .onOpenURL { url in
          GIDSignIn.sharedInstance.handle(url)
        }
    }
  }
}

UIKit

  1. Откройте файл AppDelegate.swift .
  2. Добавьте следующий импорт в начало файла:
import GoogleSignIn
  1. Добавьте следующую функцию обработчика аутентификации в класс AppDelegate . Лучше всего разместить её непосредственно после закрывающей фигурной скобки метода application(_:didFinishLaunchingWithOptions:) :
func application(
  _ app: UIApplication,
  open url: URL,
  options: [UIApplication.OpenURLOptionsKey: Any] = [:]
) -> Bool {
  var handled: Bool

  handled = GIDSignIn.sharedInstance.handle(url)
  if handled {
    return true
  }
  // If not handled by this app, return false.
  return false
}

После внесения этих изменений ваш файл AppDelegate.swift должен выглядеть следующим образом:

import GoogleSignIn
import UIKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

  func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    // Override point for customization after application launch.
    return true
  }

  func application(
    _ app: UIApplication,
    open url: URL,
    options: [UIApplication.OpenURLOptionsKey: Any] = [:]
  ) -> Bool {
    var handled: Bool

    handled = GIDSignIn.sharedInstance.handle(url)
    if handled {
      return true
    }
    // If not handled by this app, return false.
    return false
  }

  // MARK: UISceneSession Lifecycle

  func application(
    _ application: UIApplication,
    configurationForConnecting connectingSceneSession: UISceneSession,
    options: UIScene.ConnectionOptions
  ) -> UISceneConfiguration {
    // Called when a new scene session is being created.
    // Use this method to select a configuration to create the new scene with.
    return UISceneConfiguration(
      name: "Default Configuration",
      sessionRole: connectingSceneSession.role
    )
  }

  func application(
    _ application: UIApplication,
    didDiscardSceneSessions sceneSessions: Set<UISceneSession>
  ) {
    // Called when the user discards a scene session.
    // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
    // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
  }
}

Протестируйте процесс входа в систему.

Теперь вы можете протестировать весь процесс авторизации!

Запустите приложение и нажмите кнопку входа. После аутентификации Google отобразит экран согласия, где вы сможете предоставить приложению разрешение на доступ к вашей информации. После вашего одобрения вход будет завершен, и вы вернетесь в приложение.

После успешного завершения процесса авторизации SDK «Вход через Google» надежно сохраняет учетные данные пользователя в связке ключей устройства. Эти учетные данные можно использовать позже, чтобы пользователь оставался авторизованным при последующих запусках приложений.

8. Добавьте кнопку выхода из системы.

Теперь, когда вход в систему работает, следующим шагом будет добавление кнопки выхода и обновление пользовательского интерфейса в соответствии с текущим состоянием входа пользователя. При успешном входе в систему SDK предоставляет объект GIDGoogleUser . Этот объект содержит свойство profile с основной информацией, такой как имя и адрес электронной почты пользователя, которые вы будете использовать для персонализации пользовательского интерфейса.

SwiftUI

  1. Откройте файл ContentView.swift .
  2. Добавьте переменную состояния в начало структуры ContentView . Эта переменная будет хранить информацию о пользователе после его входа в систему. Поскольку это переменная @State , SwiftUI будет автоматически обновлять ваш пользовательский интерфейс при изменении её значения:
struct ContentView: View {
  @State private var user: GIDGoogleUser?
}
  1. Замените текущее body вашей структуры ContentView следующим кодом VStack . Это проверит, содержит ли переменная состояния user пользователя. Если да, отобразится приветственное сообщение и кнопка выхода. Если нет, отобразится исходная кнопка «Войти через Google»:
var body: some View {
  VStack {
    // Check if the user is signed in.
    if let user = user {
      // If signed in, show a welcome message and the sign-out button.
      Text("Hello, \(user.profile?.givenName ?? "User")!")
        .font(.title)
        .padding()

      Button("Sign Out", action: signOut)
        .buttonStyle(.borderedProminent)

    } else {
      // If not signed in, show the "Sign in with Google" button.
      GoogleSignInButton(
        scheme: .dark,  // Options: .light, .dark, .auto
        style: .standard,  // Options: .standard, .wide, .icon
        state: .normal,  // Options: .normal, .disabled
        action: handleSignInButton
      ).padding()
    }
  }
}
  1. Обновите блок завершения handleSignInButton , присвоив значение signInResult.user вашей новой переменной user . Именно это приводит к переключению пользовательского интерфейса в режим авторизации:
func handleSignInButton() {
  // Find the current window scene.
  guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene else {
    print("There is no active window scene")
    return
  }

  // Get the root view controller from the window scene.
  guard
    let rootViewController = windowScene.windows.first(where: { $0.isKeyWindow })?
      .rootViewController
  else {
    print("There is no key window or root view controller")
    return
  }

  // Start the sign-in process.
  GIDSignIn.sharedInstance.signIn(
    withPresenting: rootViewController
  ) { signInResult, error in
    guard let result = signInResult else {
      // Inspect error
      print("Error signing in: \(error?.localizedDescription ?? "No error description")")
      return
    }

    DispatchQueue.main.async {
      self.user = result.user
    }

    // If sign in succeeded, display the app's main content View.
    print("ID Token: \(result.user.idToken?.tokenString ?? "")")
  }
}
  1. Добавьте в конец структуры ContentView новую функцию signOut , которая будет вызываться кнопкой выхода:
func signOut() {
  GIDSignIn.sharedInstance.signOut()
  // After signing out, set the `user` state variable to `nil`.
  self.user = nil
}

Запустите приложение и войдите в систему. После успешной аутентификации вы должны увидеть изменения в пользовательском интерфейсе!

Состояние авторизации в фреймворке SwiftUI на симуляторе iOS

После внесения этих изменений ваш файл ContentView.swift должен выглядеть следующим образом:

import GoogleSignIn
import GoogleSignInSwift
import SwiftUI

struct ContentView: View {

  @State private var user: GIDGoogleUser?

  var body: some View {
    VStack {
      // Check if the user is signed in.
      if let user = user {
        // If signed in, show a welcome message and the sign-out button.
        Text("Hello, \(user.profile?.givenName ?? "User")!")
          .font(.title)
          .padding()

        Button("Sign Out", action: signOut)
          .buttonStyle(.borderedProminent)

      } else {
        // If not signed in, show the "Sign in with Google" button.
        GoogleSignInButton(
          scheme: .dark,  // Options: .light, .dark, .auto
          style: .standard,  // Options: .standard, .wide, .icon
          state: .normal,  // Options: .normal, .disabled
          action: handleSignInButton
        ).padding()
      }
    }
  }

  func handleSignInButton() {
    // Find the current window scene.
    guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene else {
      print("There is no active window scene")
      return
    }

    // Get the root view controller from the window scene.
    guard
      let rootViewController = windowScene.windows.first(where: { $0.isKeyWindow })?
        .rootViewController
    else {
      print("There is no key window or root view controller")
      return
    }

    // Start the sign-in process.
    GIDSignIn.sharedInstance.signIn(
      withPresenting: rootViewController
    ) { signInResult, error in
      guard let result = signInResult else {
        // Inspect error
        print("Error signing in: \(error?.localizedDescription ?? "No error description")")
        return
      }

      DispatchQueue.main.async {
        self.user = result.user
      }

      // If sign in succeeded, display the app's main content View.
      print("ID Token: \(result.user.idToken?.tokenString ?? "")")
    }
  }

  func signOut() {
    GIDSignIn.sharedInstance.signOut()
    // After signing out, set the `user` state variable to `nil`.
    self.user = nil
  }
}

#Preview {
  ContentView()
}

UIKit

  1. Откройте файл ViewController.swift .
  2. В верхней части ViewController , непосредственно под местом, где вы объявили signInButton , добавьте кнопку выхода и приветственную надпись:
let signOutButton = UIButton(type: .system)
let welcomeLabel = UILabel()
  1. Добавьте следующую функцию в конец ViewController . Эта функция будет отображать пользователю разный пользовательский интерфейс в зависимости от статуса его входа в систему:
private func updateUI(for user: GIDGoogleUser?) {
  if let user = user {
    // User is signed in.
    signInButton.isHidden = true
    signOutButton.isHidden = false
    welcomeLabel.isHidden = false
    welcomeLabel.text = "Hello, \(user.profile?.givenName ?? "User")!"
  } else {
    // User is signed out.
    signInButton.isHidden = false
    signOutButton.isHidden = true
    welcomeLabel.isHidden = true
  }
}
  1. В конце функции viewDidLoad добавьте следующий код, чтобы добавить в представление приветственную метку и кнопку выхода:
// --- Set up the Welcome Label ---
welcomeLabel.translatesAutoresizingMaskIntoConstraints = false
welcomeLabel.textAlignment = .center
welcomeLabel.font = .systemFont(ofSize: 24, weight: .bold)
view.addSubview(welcomeLabel)

NSLayoutConstraint.activate([
  welcomeLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
  welcomeLabel.bottomAnchor.constraint(equalTo: signInButton.topAnchor, constant: -20),
])

// --- Set up the Sign-Out Button ---
signOutButton.translatesAutoresizingMaskIntoConstraints = false
signOutButton.setTitle("Sign Out", for: .normal)
view.addSubview(signOutButton)

NSLayoutConstraint.activate([
  signOutButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
  signOutButton.topAnchor.constraint(equalTo: signInButton.bottomAnchor, constant: 20),
])

signOutButton.addTarget(self, action: #selector(signOutButtonTapped), for: .touchUpInside)

// --- Set Initial UI State ---
updateUI(for: nil)
  1. Обновите функцию signInButtonTapped , чтобы она вызывала метод UpdateUI при успешном входе в систему:
@objc func signInButtonTapped() {
  // Start the sign-in process.
  GIDSignIn.sharedInstance.signIn(withPresenting: self) { signInResult, error in
    guard let result = signInResult else {
      // Inspect error
      print("Error signing in: \(error?.localizedDescription ?? "No error description")")
      return
    }

    // If sign in succeeded, print the ID token.
    print("ID Token: \(result.user.idToken?.tokenString ?? "")")

    DispatchQueue.main.async {
      self.updateUI(for: result.user)
    }
  }
}
  1. Наконец, добавьте в ViewController функцию signOutButtonTapped для обработки процесса выхода из системы:
@objc func signOutButtonTapped() {
  GIDSignIn.sharedInstance.signOut()
  // Update the UI for the signed-out state.
  updateUI(for: nil)
}

Запустите приложение и войдите в систему. После успешной аутентификации вы должны увидеть изменения в пользовательском интерфейсе!

Состояние авторизации в фреймворке UIKit на симуляторе iOS

После внесения этих изменений ваш файл ViewController.swift должен выглядеть следующим образом:

import GoogleSignIn
import UIKit

class ViewController: UIViewController {

  // Create an instance of the Sign in with Google button
  let signInButton = GIDSignInButton()
  let signOutButton = UIButton(type: .system)
  let welcomeLabel = UILabel()

  override func viewDidLoad() {
    super.viewDidLoad()

    // Set the width and color of the sign-in button
    signInButton.style = .standard  // Options: .standard, .wide, .iconOnly
    signInButton.colorScheme = .dark  // Options: .dark, .light

    // Add the sign-in button to your view
    view.addSubview(signInButton)

    // Position the button using constraints
    signInButton.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
      signInButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
      signInButton.centerYAnchor.constraint(equalTo: view.centerYAnchor),
    ])

    // Add a target to the button to call a method when it's pressed
    signInButton.addTarget(self, action: #selector(signInButtonTapped), for: .touchUpInside)

    // --- Set up the Welcome Label ---
    welcomeLabel.translatesAutoresizingMaskIntoConstraints = false
    welcomeLabel.textAlignment = .center
    welcomeLabel.font = .systemFont(ofSize: 24, weight: .bold)
    view.addSubview(welcomeLabel)

    NSLayoutConstraint.activate([
      welcomeLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
      welcomeLabel.bottomAnchor.constraint(equalTo: signInButton.topAnchor, constant: -20),
    ])

    // --- Set up the Sign-Out Button ---
    signOutButton.translatesAutoresizingMaskIntoConstraints = false
    signOutButton.setTitle("Sign Out", for: .normal)
    view.addSubview(signOutButton)

    NSLayoutConstraint.activate([
      signOutButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
      signOutButton.topAnchor.constraint(equalTo: signInButton.bottomAnchor, constant: 20),
    ])

    signOutButton.addTarget(self, action: #selector(signOutButtonTapped), for: .touchUpInside)

    // --- Set Initial UI State ---
    updateUI(for: nil)
  }

  // This method is called when the sign-in button is pressed.
  @objc func signInButtonTapped() {
    // Start the sign-in process.
    GIDSignIn.sharedInstance.signIn(withPresenting: self) { signInResult, error in
      guard let result = signInResult else {
        // Inspect error
        print("Error signing in: \(error?.localizedDescription ?? "No error description")")
        return
      }

      // If sign in succeeded, print the ID token.
      print("ID Token: \(result.user.idToken?.tokenString ?? "")")

      DispatchQueue.main.async {
        self.updateUI(for: result.user)
      }
    }
  }

  private func updateUI(for user: GIDGoogleUser?) {
    if let user = user {
      // User is signed in.
      signInButton.isHidden = true
      signOutButton.isHidden = false
      welcomeLabel.isHidden = false
      welcomeLabel.text = "Hello, \(user.profile?.givenName ?? "User")!"
    } else {
      // User is signed out.
      signInButton.isHidden = false
      signOutButton.isHidden = true
      welcomeLabel.isHidden = true
    }
  }

  @objc func signOutButtonTapped() {
    GIDSignIn.sharedInstance.signOut()
    // Update the UI for the signed-out state.
    updateUI(for: nil)
  }
}

9. Восстановить состояние авторизации пользователя.

Для улучшения пользовательского опыта при повторном использовании приложения следующим шагом является восстановление состояния авторизации при запуске приложения. Вызов функции restorePreviousSignIn использует учетные данные, сохраненные в связке ключей, для автоматического повторного входа пользователя в систему, гарантируя, что ему не придется каждый раз проходить процедуру авторизации.

SwiftUI

  1. Откройте файл ContentView.swift .
  2. Добавьте следующий код непосредственно после VStack внутри переменной body :
.onAppear {
  // On appear, try to restore a previous sign-in.
  GIDSignIn.sharedInstance.restorePreviousSignIn { user, error in
    // This closure is called when the restoration is complete.
    if let user = user {
      // If a user was restored, update the `user` state variable.
      DispatchQueue.main.async {
        self.user = user
      }

      // Print the ID token to the console when restored.
      print("Restored ID Token: \(user.idToken?.tokenString ?? "")")
    }
  }
}

Ваш файл ContentView.swift должен выглядеть примерно так:

import GoogleSignIn
import GoogleSignInSwift
import SwiftUI

struct ContentView: View {

  @State private var user: GIDGoogleUser?

  var body: some View {
    VStack {
      // Check if the user is signed in.
      if let user = user {
        // If signed in, show a welcome message and the sign-out button.
        Text("Hello, \(user.profile?.givenName ?? "User")!")
          .font(.title)
          .padding()

        Button("Sign Out", action: signOut)
          .buttonStyle(.borderedProminent)

      } else {
        // If not signed in, show the "Sign in with Google" button.
        GoogleSignInButton(
          scheme: .dark,  // Options: .light, .dark, .auto
          style: .standard,  // Options: .standard, .wide, .icon
          state: .normal,  // Options: .normal, .disabled
          action: handleSignInButton
        ).padding()
      }
    }

    .onAppear {
      // On appear, try to restore a previous sign-in.
      GIDSignIn.sharedInstance.restorePreviousSignIn { user, error in
        // This closure is called when the restoration is complete.
        if let user = user {
          // If a user was restored, update the `user` state variable.
          DispatchQueue.main.async {
            self.user = user
          }

          // Print the ID token to the console when restored.
          print("Restored ID Token: \(user.idToken?.tokenString ?? "")")
        }
      }
    }
  }

  func handleSignInButton() {
    // Find the current window scene.
    guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene else {
      print("There is no active window scene")
      return
    }

    // Get the root view controller from the window scene.
    guard
      let rootViewController = windowScene.windows.first(where: { $0.isKeyWindow })?
        .rootViewController
    else {
      print("There is no key window or root view controller")
      return
    }

    // Start the sign-in process.
    GIDSignIn.sharedInstance.signIn(
      withPresenting: rootViewController
    ) { signInResult, error in
      guard let result = signInResult else {
        // Inspect error
        print("Error signing in: \(error?.localizedDescription ?? "No error description")")
        return
      }

      DispatchQueue.main.async {
        self.user = result.user
      }

      // If sign in succeeded, display the app's main content View.
      print("ID Token: \(result.user.idToken?.tokenString ?? "")")
    }
  }

  func signOut() {
    GIDSignIn.sharedInstance.signOut()
    // After signing out, set the `user` state variable to `nil`.
    self.user = nil
  }
}

#Preview {
  ContentView()
}

UIKit

  1. Откройте файл ViewController.swift .
  2. Добавьте следующий вызов restorePreviousSignIn в конец метода viewDidLoad :
// Attempt to restore a previous sign-in session
GIDSignIn.sharedInstance.restorePreviousSignIn { user, error in
  if let user = user {
    print("Successfully restored sign-in for user: \(user.profile?.givenName ?? "Unknown")")

    // Print the ID token when a session is restored.
    print("Restored ID Token: \(user.idToken?.tokenString ?? "")")

    // On success, update the UI for the signed-in state on the main thread.
    DispatchQueue.main.async {
      self.updateUI(for: user)
    }
  }
}

Ваш файл ViewController.swift должен выглядеть следующим образом:

import GoogleSignIn
import UIKit

class ViewController: UIViewController {

  // Create an instance of the Sign in with Google button
  let signInButton = GIDSignInButton()
  let signOutButton = UIButton(type: .system)
  let welcomeLabel = UILabel()

  override func viewDidLoad() {
    super.viewDidLoad()

    // Set the width and color of the sign-in button
    signInButton.style = .standard  // Options: .standard, .wide, .iconOnly
    signInButton.colorScheme = .dark  // Options: .dark, .light

    // Add the sign-in button to your view
    view.addSubview(signInButton)

    // Position the button using constraints
    signInButton.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
      signInButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
      signInButton.centerYAnchor.constraint(equalTo: view.centerYAnchor),
    ])

    // Add a target to the button to call a method when it's pressed
    signInButton.addTarget(self, action: #selector(signInButtonTapped), for: .touchUpInside)

    // --- Set up the Welcome Label ---
    welcomeLabel.translatesAutoresizingMaskIntoConstraints = false
    welcomeLabel.textAlignment = .center
    welcomeLabel.font = .systemFont(ofSize: 24, weight: .bold)
    view.addSubview(welcomeLabel)

    NSLayoutConstraint.activate([
      welcomeLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
      welcomeLabel.bottomAnchor.constraint(equalTo: signInButton.topAnchor, constant: -20),
    ])

    // --- Set up the Sign-Out Button ---
    signOutButton.translatesAutoresizingMaskIntoConstraints = false
    signOutButton.setTitle("Sign Out", for: .normal)
    view.addSubview(signOutButton)

    NSLayoutConstraint.activate([
      signOutButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
      signOutButton.topAnchor.constraint(equalTo: signInButton.bottomAnchor, constant: 20),
    ])

    signOutButton.addTarget(self, action: #selector(signOutButtonTapped), for: .touchUpInside)

    // --- Set Initial UI State ---
    updateUI(for: nil)

    // Attempt to restore a previous sign-in session
    GIDSignIn.sharedInstance.restorePreviousSignIn { user, error in
      if let user = user {
        print("Successfully restored sign-in for user: \(user.profile?.givenName ?? "Unknown")")

        // Print the ID token when a session is restored.
        print("Restored ID Token: \(user.idToken?.tokenString ?? "")")

        // On success, update the UI for the signed-in state on the main thread.
        DispatchQueue.main.async {
          self.updateUI(for: user)
        }
      }
    }
  }

  // This method is called when the sign-in button is pressed.
  @objc func signInButtonTapped() {
    // Start the sign-in process.
    GIDSignIn.sharedInstance.signIn(withPresenting: self) { signInResult, error in
      guard let result = signInResult else {
        // Inspect error
        print("Error signing in: \(error?.localizedDescription ?? "No error description")")
        return
      }

      // If sign in succeeded, print the ID token.
      print("ID Token: \(result.user.idToken?.tokenString ?? "")")

      DispatchQueue.main.async {
        self.updateUI(for: result.user)
      }
    }
  }

  private func updateUI(for user: GIDGoogleUser?) {
    if let user = user {
      // User is signed in.
      signInButton.isHidden = true
      signOutButton.isHidden = false
      welcomeLabel.isHidden = false
      welcomeLabel.text = "Hello, \(user.profile?.givenName ?? "User")!"
    } else {
      // User is signed out.
      signInButton.isHidden = false
      signOutButton.isHidden = true
      welcomeLabel.isHidden = true
    }
  }

  @objc func signOutButtonTapped() {
    GIDSignIn.sharedInstance.signOut()
    // Update the UI for the signed-out state.
    updateUI(for: nil)
  }
}

Проверьте бесшумный вход в систему.

После входа в систему полностью закройте приложение и запустите его снова. Вы увидите, что теперь вы автоматически вошли в систему, и вам не нужно будет нажимать кнопку.

10. Разберитесь в идентификационном токене.

Хотя объект GIDGoogleUser удобен для персонализации пользовательского интерфейса с использованием имени и адреса электронной почты пользователя, наиболее важными данными, возвращаемыми SDK, является идентификационный токен.

В этом практическом задании используется онлайн-инструмент для проверки содержимого JWT. В рабочем приложении вам следует отправить этот ID-токен на ваш бэкэнд-сервер. Ваш сервер должен проверить целостность ID-токена и использовать JWT для выполнения более значимых действий, таких как создание новой учетной записи на вашей бэкэнд-платформе или установление новой сессии для пользователя.

Получите доступ к токену JWT и расшифруйте его.

  1. Запустите приложение.
  2. Откройте консоль Xcode. Вы должны увидеть выведенный ID-токен. Он будет выглядеть примерно так: eyJhbGciOiJSUzI1Ni ... Hecz6Wm4Q .
  3. Скопируйте токен ID и используйте онлайн-инструмент, например jwt.io, для расшифровки JWT.

Расшифрованный JWT будет выглядеть следующим образом:

{
  "alg": "RS256",
  "kid": "c8ab71530972bba20b49f78a09c9852c43ff9118",
  "typ": "JWT"
}
{
  "iss": "https://accounts.google.com",
  "azp": "171291171076-rrbkcjrp5jbte92ai9gub115ertscphi.apps.googleusercontent.com",
  "aud": "171291171076-rrbkcjrp5jbte92ai9gub115ertscphi.apps.googleusercontent.com",
  "sub": "10769150350006150715113082367",
  "email": "example@example.com",
  "email_verified": true,
  "at_hash": "JyCYDmHtzhjkb0-qJhKsMg",
  "name": "Kimya",
  "picture": "https://lh3.googleusercontent.com/a/ACg8ocIyy4VoR31t_n0biPVcScBHwZOCRaKVDb_MoaMYep65fyqoAw=s96-c",
  "given_name": "Kimya",
  "iat": 1758645896,
  "exp": 1758649496
}

Примечательные поля токенов

Расшифрованный ID-токен содержит поля с различными назначениями. Некоторые из них легко понять, например, имя и адрес электронной почты, другие же используются вашим бэкэнд-сервером для проверки.

Особое внимание следует уделить следующей области:

  • sub : Поле sub — это уникальный, постоянный идентификатор учетной записи Google пользователя. Пользователь может изменить свой основной адрес электронной почты или имя, но его идентификатор sub никогда не изменится. Это делает поле sub идеальным значением для использования в качестве первичного ключа для учетных записей пользователей в вашей административной панели.

Получение информации о пользователе из ID-токена содержит более подробную информацию о значении всех полей токена.

11. Защитите свое приложение с помощью App Check.

Настоятельно рекомендуется включить проверку приложений (App Check), чтобы гарантировать, что доступ к конечным точкам Google OAuth 2.0 от имени вашего проекта будет иметь только ваше приложение. Проверка приложений работает путем подтверждения того, что запросы к вашим бэкэнд-сервисам исходят от вашего подлинного приложения на реальном и не модифицированном устройстве.

В этом разделе показано, как интегрировать App Check в ваше приложение и настроить его как для отладки в симуляторе, так и для производственной сборки, работающей на реальном устройстве.

Настройка консоли

Для интеграции App Check в ваше приложение требуется одноразовая настройка в консолях Google Cloud и Firebase. Это включает в себя включение App Check для вашего iOS OAuth-клиента в консоли Google Cloud, создание ключа API для использования с отладочным провайдером App Check и привязку вашего проекта Google Cloud к Firebase.

Включите проверку приложений в консоли Google Cloud.

  1. Перейдите к списку клиентов , связанных с вашим проектом в Google Cloud.
  2. Выберите идентификатор клиента OAuth 2.0, который вы создали для своего iOS-приложения.
  3. Включите флажок «Приложение» под пунктом «Google Identity» для iOS.

Страница редактирования клиента OAuth с переключателем проверки приложения.

  1. Нажмите « Сохранить ».

Создайте ключ API

  1. Перейдите на страницу библиотеки API для вашего проекта Google Cloud.
  2. Введите в строку поиска «Firebase App Check API» .

Страница библиотеки API Google Cloud Console

  1. Выберите и включите API Firebase App Check .
  2. Перейдите в раздел «API и сервисы» и выберите «Учетные данные» в меню навигации.
  3. В верхней части страницы выберите пункт «Создать учетные данные» .

Страница учетных данных API консоли Google Cloud

  1. Присвойте имя этому ключу API.
  2. В разделе «Ограничения приложений» выберите приложения iOS.
  3. Добавьте идентификатор пакета для вашего приложения в список одобренных приложений.
  4. В разделе «Ограничения API» выберите «Ограничить ключ» .
  5. Выберите Firebase App Check API из выпадающего меню.
  6. Выберите «Создать» .

Страница создания ключа API в Google Cloud Console

  1. Скопируйте созданный API-ключ. Он понадобится вам на следующем шаге.

Добавьте Firebase в свой проект Google Cloud.

  1. Перейдите в консоль Firebase .
  2. Выберите « Начать работу, создав проект Firebase» .
  3. Выберите «Добавить Firebase в проект Google Cloud» .

Добавить Firebase в существующий проект Google Cloud

  1. Выберите проект Google Cloud из выпадающего списка и продолжите процесс регистрации.
  2. Выберите «Добавить Firebase» .
  3. После того, как ваш проект Firebase будет готов, выберите «Продолжить» , чтобы открыть проект.

Интеграция клиентского кода

После настройки проекта Google Cloud для использования App Check, пришло время написать клиентский код для его активации. Поставщик, используемый для аттестации, различается в производственной и отладочной средах. Производственное приложение на реальном устройстве использует встроенный сервис App Attest от Apple для подтверждения своей подлинности. Однако, поскольку симулятор iOS не может обеспечить такой вид аттестации, в отладочной среде требуется специальный поставщик отладки, которому передается ключ API.

Приведенный ниже код обрабатывает оба сценария, используя директиву компилятора для автоматического выбора правильного поставщика во время сборки.

SwiftUI

  1. Откройте основной файл приложения.
  2. Определите следующий класс AppDelegate после импортов и перед атрибутом @main :
class AppDelegate: NSObject, UIApplicationDelegate {
  func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
  ) -> Bool {

    #if targetEnvironment(simulator)
      // Configure for debugging on a simulator.
      // TODO: Replace "YOUR_API_KEY" with the key from your Google Cloud project.
      let apiKey = "YOUR_API_KEY"
      GIDSignIn.sharedInstance.configureDebugProvider(withAPIKey: apiKey) { error in
        if let error {
          print("Error configuring GIDSignIn debug provider: \(error)")
        }
      }
    #else
      // Configure GIDSignIn for App Check on a real device.
      GIDSignIn.sharedInstance.configure { error in
        if let error {
          print("Error configuring GIDSignIn for App Check: \(error)")
        } else {
          print("GIDSignIn configured for App Check.")
        }
      }
    #endif

    return true
  }
}
  1. Замените "YOUR_API_KEY" в предоставленном коде на ключ API, скопированный вами из консоли Google Cloud.
  2. Добавьте следующую строку в структуру App , непосредственно перед переменной body . Это зарегистрирует ваш класс AppDelegate в жизненном цикле приложения, позволяя ему реагировать на запуск приложения и другие системные события:
@UIApplicationDelegateAdaptor(AppDelegate.self) var delegate

Основной файл вашего приложения должен выглядеть следующим образом:

import GoogleSignIn
import SwiftUI

class AppDelegate: NSObject, UIApplicationDelegate {
  func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
  ) -> Bool {

    #if targetEnvironment(simulator)
      // Configure for debugging on a simulator.
      // TODO: Replace "YOUR_API_KEY" with the key from your Google Cloud project.
      let apiKey = "YOUR_API_KEY"
      GIDSignIn.sharedInstance.configureDebugProvider(withAPIKey: apiKey) { error in
        if let error {
          print("Error configuring GIDSignIn debug provider: \(error)")
        }
      }
    #else
      // Configure GIDSignIn for App Check on a real device.
      GIDSignIn.sharedInstance.configure { error in
        if let error {
          print("Error configuring GIDSignIn for App Check: \(error)")
        } else {
          print("GIDSignIn configured for App Check.")
        }
      }
    #endif

    return true
  }
}

@main
struct iOS_Sign_in_with_Google_App: App {

  @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate

  var body: some Scene {
    WindowGroup {
      ContentView()

        .onOpenURL { url in
          GIDSignIn.sharedInstance.handle(url)
        }
    }
  }
}

UIKit

  1. Откройте файл AppDelegate.swift .
  2. Обновите метод application(_:didFinishLaunchingWithOptions:) , добавив в него инициализацию App Check:
func application(
  _ application: UIApplication,
  didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {

  #if targetEnvironment(simulator)
    // Configure for debugging on a simulator.
    // TODO: Replace "YOUR_API_KEY" with the key from your Google Cloud project.
    let apiKey = "YOUR_API_KEY"
    GIDSignIn.sharedInstance.configureDebugProvider(withAPIKey: apiKey) { error in
      if let error {
        print("Error configuring GIDSignIn debug provider: \(error)")
      }
    }
  #else
    // Configure GIDSignIn for App Check on a real device.
    GIDSignIn.sharedInstance.configure { error in
      if let error {
        print("Error configuring GIDSignIn for App Check: \(error)")
      }
    }
  #endif

  return true
}
  1. Замените "YOUR_API_KEY" в предоставленном коде на ключ API, скопированный вами из консоли Google Cloud.

Ваш файл AppDelegate.swift должен выглядеть следующим образом:

import GoogleSignIn
import UIKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

  func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {

    #if targetEnvironment(simulator)
      // Configure for debugging on a simulator.
      // TODO: Replace "YOUR_API_KEY" with the key from your Google Cloud project.
      let apiKey = "YOUR_API_KEY"
      GIDSignIn.sharedInstance.configureDebugProvider(withAPIKey: apiKey) { error in
        if let error {
          print("Error configuring GIDSignIn debug provider: \(error)")
        }
      }
    #else
      // Configure GIDSignIn for App Check on a real device.
      GIDSignIn.sharedInstance.configure { error in
        if let error {
          print("Error configuring GIDSignIn for App Check: \(error)")
        }
      }
    #endif

    return true
  }

  func application(
    _ app: UIApplication,
    open url: URL,
    options: [UIApplication.OpenURLOptionsKey: Any] = [:]
  ) -> Bool {
    var handled: Bool

    handled = GIDSignIn.sharedInstance.handle(url)
    if handled {
      return true
    }
    // If not handled by this app, return false.
    return false
  }

  // MARK: UISceneSession Lifecycle

  func application(
    _ application: UIApplication,
    configurationForConnecting connectingSceneSession: UISceneSession,
    options: UIScene.ConnectionOptions
  ) -> UISceneConfiguration {
    // Called when a new scene session is being created.
    // Use this method to select a configuration to create the new scene with.
    return UISceneConfiguration(
      name: "Default Configuration",
      sessionRole: connectingSceneSession.role
    )
  }

  func application(
    _ application: UIApplication,
    didDiscardSceneSessions sceneSessions: Set<UISceneSession>
  ) {
    // Called when the user discards a scene session.
    // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
    // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
  }
}

Проверка приложения на симуляторе

  1. В строке меню Xcode перейдите в раздел Product > Scheme > Edit Scheme .
  2. В меню навигации выберите пункт «Выполнить» .
  3. Выберите вкладку «Аргументы» .
  4. В разделе «Аргументы, передаваемые при запуске» выберите + и добавьте -FIRDebugEnabled . Этот аргумент запуска включает отладочное логирование Firebase.
  5. Выберите «Закрыть» .

страница редактора аргументов Xcode

  1. Запустите приложение на симуляторе.
  2. Скопируйте отладочный токен App Check , который отображается в консоли Xcode.

Проверка отладочного токена приложения в консоли Xcode

  1. Перейдите к своему проекту в консоли Firebase .
  2. Разверните раздел «Сборка» в меню навигации.
  3. Выберите проверку приложения .
  4. Выберите вкладку «Приложения» .
  5. Наведите курсор на приложение и выберите значок меню с тремя точками.

Проверка настроек приложения Firebase

  1. Выберите «Управление отладочными токенами» .
  2. Выберите «Добавить отладочный токен» .
  3. Присвойте своему отладочному токену имя и вставьте скопированный ранее отладочный токен в качестве значения.
  4. Выберите «Сохранить» , чтобы зарегистрировать свой токен.

Firebase App Check: управление отладочными токенами

  1. Вернитесь к симулятору и войдите в систему.

Для отображения метрик в консоли может потребоваться несколько минут. После этого вы можете убедиться в работоспособности App Check, отслеживая увеличение количества подтвержденных запросов в одном из двух мест:

  • В разделе «Проверка приложений» консоли Firebase, на вкладке «API».

Метрики Firebase App Check

  • На странице редактирования вашего OAuth-клиента в консоли Google Cloud.

Метрики проверки приложений в консоли Google Cloud

После мониторинга метрик App Check вашего приложения и подтверждения проверки легитимных запросов следует включить принудительное применение App Check . После включения App Check будет отклонять все непроверенные запросы, гарантируя, что доступ к конечным точкам Google OAuth 2.0 от имени вашего проекта будет осуществляться только от вашего подлинного приложения.

12. Дополнительные ресурсы

Поздравляем!

Вы настроили iOS-клиент OAuth 2.0, добавили кнопку «Войти через Google» в iOS-приложение, научились настраивать внешний вид кнопки, расшифровали JWT-токен ID и включили проверку приложения (App Check).

Эти ссылки могут помочь вам с дальнейшими шагами:

Часто задаваемые вопросы