Вы можете разрешить своим пользователям аутентифицироваться в Firebase, используя их учетные записи Facebook, интегрировав вход через Facebook или ограниченный вход через Facebook в свое приложение.
Прежде чем начать
Используйте Swift Package Manager для установки зависимостей Firebase и управления ими.
- В Xcode, открыв проект приложения, выберите «Файл» > «Добавить пакеты» .
- При появлении запроса добавьте репозиторий Firebase SDK для платформ Apple:
- Выберите библиотеку Firebase Authentication .
- Добавьте флаг
-ObjC
в раздел «Другие флаги компоновщика» настроек сборки вашей цели. - По завершении Xcode автоматически начнет разрешать и загружать ваши зависимости в фоновом режиме.
https://github.com/firebase/firebase-ios-sdk.git
Далее выполните несколько шагов настройки:
- На сайте Facebook для разработчиков получите идентификатор приложения и секрет приложения для своего приложения.
- Включить вход через Facebook:
- В консоли Firebase откройте раздел Auth .
- На вкладке «Метод входа» включите метод входа в Facebook и укажите идентификатор приложения и секрет приложения, полученные от Facebook.
- Затем убедитесь, что ваш URI перенаправления OAuth (например,
my-app-12345.firebaseapp.com/__/auth/handler
) указан в качестве одного из ваших URI перенаправления OAuth на странице настроек вашего приложения Facebook на сайте Facebook для разработчиков в Продукте. Настройки > Конфигурация входа в Facebook .
Внедрить вход через Facebook
Чтобы использовать «классический» вход в Facebook, выполните следующие действия. Альтернативно вы можете использовать ограниченный вход в Facebook, как показано в следующем разделе.
- Интегрируйте вход через Facebook в свое приложение, следуя документации разработчика . При инициализации объекта
FBSDKLoginButton
настройте делегата для получения событий входа и выхода из системы. Например: В своем делегате реализуйтеlet loginButton = FBSDKLoginButton()
loginButton.delegate = selfFBSDKLoginButton *loginButton = [[FBSDKLoginButton alloc] init];
loginButton.delegate = self;didCompleteWithResult:error:
.func loginButton(_ loginButton: FBSDKLoginButton!, didCompleteWith result: FBSDKLoginManagerLoginResult!, error: Error!) {
if let error = error {
print(error.localizedDescription)
return
}
// ...
}- (void)loginButton:(FBSDKLoginButton *)loginButton
didCompleteWithResult:(FBSDKLoginManagerLoginResult *)result
error:(NSError *)error {
if (error == nil) {
// ...
} else {
NSLog(error.localizedDescription);
}
} - Импортируйте модуль
FirebaseCore
в свойUIApplicationDelegate
, а также любые другие модули Firebase, которые использует ваш делегат приложения. Например, чтобы использовать Cloud Firestore и Authentication :import SwiftUI
import FirebaseCore
import FirebaseFirestore
import FirebaseAuth
// ...
import FirebaseCore
import FirebaseFirestore
import FirebaseAuth
// ...
@import FirebaseCore;
@import FirebaseFirestore;
@import FirebaseAuth;
// ...
- Настройте общий экземпляр
FirebaseApp
в методеapplication(_:didFinishLaunchingWithOptions:)
делегата вашего приложения:// Use Firebase library to configure APIs
FirebaseApp.configure()// Use Firebase library to configure APIs
FirebaseApp.configure()// Use Firebase library to configure APIs
[FIRApp configure]; - Если вы используете SwiftUI, вам необходимо создать делегат приложения и прикрепить его к структуре вашего
App
черезUIApplicationDelegateAdaptor
илиNSApplicationDelegateAdaptor
. Вы также должны отключить переключение делегатов приложений. Дополнительную информацию смотрите в инструкциях SwiftUI .@main
struct YourApp: App {
// register app delegate for Firebase setup
@UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
var body: some Scene {
WindowGroup {
NavigationView {
ContentView()
}
}
}
}
- После успешного входа пользователя в вашу реализацию
didCompleteWithResult:error:
получите токен доступа для вошедшего в систему пользователя и замените его на учетные данные Firebase:let credential = FacebookAuthProvider
.credential(withAccessToken: AccessToken.current!.tokenString)FIRAuthCredential *credential = [FIRFacebookAuthProvider
credentialWithAccessToken:[FBSDKAccessToken currentAccessToken].tokenString];
Внедрить ограниченный вход в Facebook
Чтобы использовать ограниченный вход в Facebook вместо «классического» входа в Facebook, выполните следующие действия.
- Интегрируйте Facebook Limited Login в свое приложение, следуя документации разработчика .
- Для каждого запроса на вход сгенерируйте уникальную случайную строку — «nonce», — которую вы будете использовать, чтобы убедиться, что полученный вами токен идентификатора был предоставлен именно в ответ на запрос аутентификации вашего приложения. Этот шаг важен для предотвращения атак повторного воспроизведения. Вы можете создать криптографически безопасный одноразовый номер с помощью
SecRandomCopyBytes(_:_:_)
, как в следующем примере: Вы отправите хэш SHA-256 nonce вместе с запросом на вход, который Facebook передаст в ответе без изменений. Firebase проверяет ответ, хешируя исходный nonce и сравнивая его со значением, переданным Facebook.private func randomNonceString(length: Int = 32) -> String {
precondition(length > 0)
var randomBytes = [UInt8](repeating: 0, count: length)
let errorCode = SecRandomCopyBytes(kSecRandomDefault, randomBytes.count, &randomBytes)
if errorCode != errSecSuccess {
fatalError(
"Unable to generate nonce. SecRandomCopyBytes failed with OSStatus \(errorCode)"
)
}
let charset: [Character] =
Array("0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._")
let nonce = randomBytes.map { byte in
// Pick a random character from the set, wrapping around if needed.
charset[Int(byte) % charset.count]
}
return String(nonce)
}
// Adapted from https://auth0.com/docs/api-auth/tutorials/nonce#generate-a-cryptographically-random-nonce
- (NSString *)randomNonce:(NSInteger)length {
NSAssert(length > 0, @"Expected nonce to have positive length");
NSString *characterSet = @"0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._";
NSMutableString *result = [NSMutableString string];
NSInteger remainingLength = length;
while (remainingLength > 0) {
NSMutableArray *randoms = [NSMutableArray arrayWithCapacity:16];
for (NSInteger i = 0; i < 16; i++) {
uint8_t random = 0;
int errorCode = SecRandomCopyBytes(kSecRandomDefault, 1, &random);
NSAssert(errorCode == errSecSuccess, @"Unable to generate nonce: OSStatus %i", errorCode);
[randoms addObject:@(random)];
}
for (NSNumber *random in randoms) {
if (remainingLength == 0) {
break;
}
if (random.unsignedIntValue < characterSet.length) {
unichar character = [characterSet characterAtIndex:random.unsignedIntValue];
[result appendFormat:@"%C", character];
remainingLength--;
}
}
}
return [result copy];
}
@available(iOS 13, *)
private func sha256(_ input: String) -> String {
let inputData = Data(input.utf8)
let hashedData = SHA256.hash(data: inputData)
let hashString = hashedData.compactMap {
String(format: "%02x", $0)
}.joined()
return hashString
}
- (NSString *)stringBySha256HashingString:(NSString *)input {
const char *string = [input UTF8String];
unsigned char result[CC_SHA256_DIGEST_LENGTH];
CC_SHA256(string, (CC_LONG)strlen(string), result);
NSMutableString *hashed = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH * 2];
for (NSInteger i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) {
[hashed appendFormat:@"%02x", result[i]];
}
return hashed;
}
- При настройке
FBSDKLoginButton
настройте делегата для получения событий входа и выхода из системы, установите режим отслеживанияFBSDKLoginTrackingLimited
и прикрепите одноразовый номер. Например: В своем делегате реализуйтеfunc setupLoginButton() {
let nonce = randomNonceString()
currentNonce = nonce
loginButton.delegate = self
loginButton.loginTracking = .limited
loginButton.nonce = sha256(nonce)
}
- (void)setupLoginButton {
NSString *nonce = [self randomNonce:32];
self.currentNonce = nonce;
self.loginButton.delegate = self;
self.loginButton.loginTracking = FBSDKLoginTrackingLimited
self.loginButton.nonce = [self stringBySha256HashingString:nonce];
}
didCompleteWithResult:error:
.func loginButton(_ loginButton: FBSDKLoginButton!, didCompleteWith result: FBSDKLoginManagerLoginResult!, error: Error!) {
if let error = error {
print(error.localizedDescription)
return
}
// ...
}
- (void)loginButton:(FBSDKLoginButton *)loginButton
didCompleteWithResult:(FBSDKLoginManagerLoginResult *)result
error:(NSError *)error {
if (error == nil) {
// ...
} else {
NSLog(error.localizedDescription);
}
}
- Импортируйте модуль
FirebaseCore
в свойUIApplicationDelegate
, а также любые другие модули Firebase, которые использует ваш делегат приложения. Например, чтобы использовать Cloud Firestore и Authentication :import SwiftUI
import FirebaseCore
import FirebaseFirestore
import FirebaseAuth
// ...
import FirebaseCore
import FirebaseFirestore
import FirebaseAuth
// ...
@import FirebaseCore;
@import FirebaseFirestore;
@import FirebaseAuth;
// ...
- Настройте общий экземпляр
FirebaseApp
в методеapplication(_:didFinishLaunchingWithOptions:)
делегата вашего приложения:// Use Firebase library to configure APIs
FirebaseApp.configure()// Use Firebase library to configure APIs
FirebaseApp.configure()// Use Firebase library to configure APIs
[FIRApp configure]; - Если вы используете SwiftUI, вам необходимо создать делегат приложения и прикрепить его к структуре вашего
App
черезUIApplicationDelegateAdaptor
илиNSApplicationDelegateAdaptor
. Вы также должны отключить переключение делегатов приложений. Дополнительную информацию смотрите в инструкциях SwiftUI .@main
struct YourApp: App {
// register app delegate for Firebase setup
@UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
var body: some Scene {
WindowGroup {
NavigationView {
ContentView()
}
}
}
}
- После успешного входа пользователя в вашу реализацию
didCompleteWithResult:error:
используйте токен идентификатора из ответа Facebook с нехешированным одноразовым номером, чтобы получить учетные данные Firebase:// Initialize a Firebase credential.
let idTokenString = AuthenticationToken.current?.tokenString
let nonce = currentNonce
let credential = OAuthProvider.credential(withProviderID: "facebook.com",
idToken: idTokenString!,
rawNonce: nonce)
// Initialize a Firebase credential.
NSString *idTokenString = FBSDKAuthenticationToken.currentAuthenticationToken.tokenString;
NSString *rawNonce = self.currentNonce;
FIROAuthCredential *credential = [FIROAuthProvider credentialWithProviderID:@"facebook.com"
IDToken:idTokenString
rawNonce:rawNonce];
Аутентификация с помощью Firebase
Наконец, пройдите аутентификацию в Firebase, используя учетные данные Firebase:
Auth.auth().signIn(with: credential) { authResult, error in
if let error = error {
let authError = error as NSError
if isMFAEnabled, authError.code == AuthErrorCode.secondFactorRequired.rawValue {
// The user is a multi-factor user. Second factor challenge is required.
let resolver = authError
.userInfo[AuthErrorUserInfoMultiFactorResolverKey] as! MultiFactorResolver
var displayNameString = ""
for tmpFactorInfo in resolver.hints {
displayNameString += tmpFactorInfo.displayName ?? ""
displayNameString += " "
}
self.showTextInputPrompt(
withMessage: "Select factor to sign in\n\(displayNameString)",
completionBlock: { userPressedOK, displayName in
var selectedHint: PhoneMultiFactorInfo?
for tmpFactorInfo in resolver.hints {
if displayName == tmpFactorInfo.displayName {
selectedHint = tmpFactorInfo as? PhoneMultiFactorInfo
}
}
PhoneAuthProvider.provider()
.verifyPhoneNumber(with: selectedHint!, uiDelegate: nil,
multiFactorSession: resolver
.session) { verificationID, error in
if error != nil {
print(
"Multi factor start sign in failed. Error: \(error.debugDescription)"
)
} else {
self.showTextInputPrompt(
withMessage: "Verification code for \(selectedHint?.displayName ?? "")",
completionBlock: { userPressedOK, verificationCode in
let credential: PhoneAuthCredential? = PhoneAuthProvider.provider()
.credential(withVerificationID: verificationID!,
verificationCode: verificationCode!)
let assertion: MultiFactorAssertion? = PhoneMultiFactorGenerator
.assertion(with: credential!)
resolver.resolveSignIn(with: assertion!) { authResult, error in
if error != nil {
print(
"Multi factor finanlize sign in failed. Error: \(error.debugDescription)"
)
} else {
self.navigationController?.popViewController(animated: true)
}
}
}
)
}
}
}
)
} else {
self.showMessagePrompt(error.localizedDescription)
return
}
// ...
return
}
// User is signed in
// ...
}
[[FIRAuth auth] signInWithCredential:credential
completion:^(FIRAuthDataResult * _Nullable authResult,
NSError * _Nullable error) {
if (isMFAEnabled && error && error.code == FIRAuthErrorCodeSecondFactorRequired) {
FIRMultiFactorResolver *resolver = error.userInfo[FIRAuthErrorUserInfoMultiFactorResolverKey];
NSMutableString *displayNameString = [NSMutableString string];
for (FIRMultiFactorInfo *tmpFactorInfo in resolver.hints) {
[displayNameString appendString:tmpFactorInfo.displayName];
[displayNameString appendString:@" "];
}
[self showTextInputPromptWithMessage:[NSString stringWithFormat:@"Select factor to sign in\n%@", displayNameString]
completionBlock:^(BOOL userPressedOK, NSString *_Nullable displayName) {
FIRPhoneMultiFactorInfo* selectedHint;
for (FIRMultiFactorInfo *tmpFactorInfo in resolver.hints) {
if ([displayName isEqualToString:tmpFactorInfo.displayName]) {
selectedHint = (FIRPhoneMultiFactorInfo *)tmpFactorInfo;
}
}
[FIRPhoneAuthProvider.provider
verifyPhoneNumberWithMultiFactorInfo:selectedHint
UIDelegate:nil
multiFactorSession:resolver.session
completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) {
if (error) {
[self showMessagePrompt:error.localizedDescription];
} else {
[self showTextInputPromptWithMessage:[NSString stringWithFormat:@"Verification code for %@", selectedHint.displayName]
completionBlock:^(BOOL userPressedOK, NSString *_Nullable verificationCode) {
FIRPhoneAuthCredential *credential =
[[FIRPhoneAuthProvider provider] credentialWithVerificationID:verificationID
verificationCode:verificationCode];
FIRMultiFactorAssertion *assertion = [FIRPhoneMultiFactorGenerator assertionWithCredential:credential];
[resolver resolveSignInWithAssertion:assertion completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) {
if (error) {
[self showMessagePrompt:error.localizedDescription];
} else {
NSLog(@"Multi factor finanlize sign in succeeded.");
}
}];
}];
}
}];
}];
}
else if (error) {
// ...
return;
}
// User successfully signed in. Get user data from the FIRUser object
if (authResult == nil) { return; }
FIRUser *user = authResult.user;
// ...
}];
Следующие шаги
После того, как пользователь входит в систему в первый раз, создается новая учетная запись пользователя, которая связывается с учетными данными (то есть именем пользователя и паролем, номером телефона или информацией поставщика аутентификации), с которыми пользователь вошел в систему. Эта новая учетная запись хранится как часть вашего проекта Firebase и может использоваться для идентификации пользователя в каждом приложении вашего проекта, независимо от того, как пользователь входит в систему.
В своих приложениях вы можете получить базовую информацию профиля пользователя из объекта
User
. См. Управление пользователями .В правилах безопасности Firebase Realtime Database и Cloud Storage Firebase вы можете получить уникальный идентификатор пользователя, вошедшего в систему, из переменной
auth
и использовать его для управления тем, к каким данным пользователь может получить доступ.
Вы можете разрешить пользователям входить в ваше приложение с использованием нескольких поставщиков аутентификации , привязав учетные данные поставщика аутентификации к существующей учетной записи пользователя.
Чтобы выйти из системы, вызовите signOut:
.
let firebaseAuth = Auth.auth()
do {
try firebaseAuth.signOut()
} catch let signOutError as NSError {
print("Error signing out: %@", signOutError)
}
NSError *signOutError;
BOOL status = [[FIRAuth auth] signOut:&signOutError];
if (!status) {
NSLog(@"Error signing out: %@", signOutError);
return;
}
Вы также можете добавить код обработки ошибок для всего спектра ошибок аутентификации. См. Обработка ошибок .