Facebook ログインまたは Facebook Limited Login をアプリに統合することで、ユーザーが Facebook アカウントを使用して Firebase で認証できるようにすることができます。
あなたが始める前に
Swift Package Manager を使用して、Firebase の依存関係をインストールおよび管理します。
- Xcode で、アプリ プロジェクトを開いた状態で、 File > Add Packagesに移動します。
- プロンプトが表示されたら、Firebase Apple プラットフォーム SDK リポジトリを追加します。
- Firebase Authentication ライブラリを選択します。
- 完了すると、Xcode はバックグラウンドで依存関係の解決とダウンロードを自動的に開始します。
https://github.com/firebase/firebase-ios-sdk
次に、いくつかの構成手順を実行します。
- Facebook for Developersサイトで、アプリのアプリ IDとアプリ シークレットを取得します。
- Facebook ログインを有効にする:
- Firebase コンソールで、 Authセクションを開きます。
- [サインイン方法] タブで、 Facebookサインイン方法を有効にし、Facebook から取得したアプリ IDとアプリ シークレットを指定します。
- 次に、 OAuth リダイレクト URI (例:
my-app-12345.firebaseapp.com/__/auth/handler
) が、製品のFacebook for Developersサイトの Facebook アプリの設定ページにOAuth リダイレクト URIの 1 つとしてリストされていることを確認します。設定 > Facebook ログイン設定。
Facebook ログインの実装
「従来の」Facebook ログインを使用するには、次の手順を実行します。または、次のセクションに示すように、Facebook Limited Login を使用することもできます。
- 開発者のドキュメントに従って、Facebook ログインをアプリに統合します。
FBSDKLoginButton
オブジェクトを初期化するときに、ログイン イベントとログアウト イベントを受け取るデリゲートを設定します。例えば:デリゲートで、迅速
let loginButton = FBSDKLoginButton() loginButton.delegate = self
Objective-C
FBSDKLoginButton *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 } // ... }
Objective-C
- (void)loginButton:(FBSDKLoginButton *)loginButton didCompleteWithResult:(FBSDKLoginManagerLoginResult *)result error:(NSError *)error { if (error == nil) { // ... } else { NSLog(error.localizedDescription); } }
-
UIApplicationDelegate
にFirebaseCore
モジュールをインポートし、アプリ デリゲートが使用する他のすべてのFirebase モジュールをインポートします。たとえば、Cloud Firestore と認証を使用するには:SwiftUI
import SwiftUI import FirebaseCore import FirebaseFirestore import FirebaseAuth // ...
迅速
import FirebaseCore import FirebaseFirestore import FirebaseAuth // ...
Objective-C
@import FirebaseCore; @import FirebaseFirestore; @import FirebaseAuth; // ...
- アプリ デリゲートの
application(_:didFinishLaunchingWithOptions:)
メソッドでFirebaseApp
共有インスタンスを構成します。SwiftUI
// Use Firebase library to configure APIs FirebaseApp.configure()
迅速
// Use Firebase library to configure APIs FirebaseApp.configure()
Objective-C
// Use Firebase library to configure APIs [FIRApp configure];
- SwiftUI を使用している場合は、アプリケーション デリゲートを作成し、それを
UIApplicationDelegateAdaptor
またはNSApplicationDelegateAdaptor
を介してApp
構造体にアタッチする必要があります。アプリ デリゲート スウィズリングも無効にする必要があります。詳細については、 SwiftUI の説明を参照してください。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)
Objective-C
FIRAuthCredential *credential = [FIRFacebookAuthProvider credentialWithAccessToken:[FBSDKAccessToken currentAccessToken].tokenString];
Facebook限定ログインの実装
「従来の」Facebook ログインの代わりに Facebook 限定ログインを使用するには、次の手順を実行します。
- 開発者のドキュメントに従って、Facebook Limited Login をアプリに統合します。
- サインイン リクエストごとに、固有のランダムな文字列 (「ナンス」) を生成します。これを使用して、取得した ID トークンがアプリの認証リクエストに応じて付与されたものであることを確認します。この手順は、リプレイ攻撃を防ぐために重要です。次の例のように、
SecRandomCopyBytes(_:_:_)
を使用して暗号的に安全なノンスを生成できます。サインイン リクエストで nonce の SHA-256 ハッシュを送信すると、Facebook はこれを変更せずに応答で渡します。 Firebase は、元のナンスをハッシュし、それを Facebook から渡された値と比較することによって、応答を検証します。迅速
// Adapted from https://auth0.com/docs/api-auth/tutorials/nonce#generate-a-cryptographically-random-nonce private func randomNonceString(length: Int = 32) -> String { precondition(length > 0) let charset: [Character] = Array("0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._") var result = "" var remainingLength = length while remainingLength > 0 { let randoms: [UInt8] = (0 ..< 16).map { _ in var random: UInt8 = 0 let errorCode = SecRandomCopyBytes(kSecRandomDefault, 1, &random) if errorCode != errSecSuccess { fatalError( "Unable to generate nonce. SecRandomCopyBytes failed with OSStatus \(errorCode)" ) } return random } randoms.forEach { random in if remainingLength == 0 { return } if random < charset.count { result.append(charset[Int(random)]) remainingLength -= 1 } } } return result }
Objective-C
// 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 }
Objective-C
- (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) }
Objective-C
- (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 } // ... }
Objective-C
- (void)loginButton:(FBSDKLoginButton *)loginButton didCompleteWithResult:(FBSDKLoginManagerLoginResult *)result error:(NSError *)error { if (error == nil) { // ... } else { NSLog(error.localizedDescription); } }
-
UIApplicationDelegate
にFirebaseCore
モジュールをインポートし、アプリ デリゲートが使用する他のすべてのFirebase モジュールをインポートします。たとえば、Cloud Firestore と認証を使用するには:SwiftUI
import SwiftUI import FirebaseCore import FirebaseFirestore import FirebaseAuth // ...
迅速
import FirebaseCore import FirebaseFirestore import FirebaseAuth // ...
Objective-C
@import FirebaseCore; @import FirebaseFirestore; @import FirebaseAuth; // ...
- アプリ デリゲートの
application(_:didFinishLaunchingWithOptions:)
メソッドでFirebaseApp
共有インスタンスを構成します。SwiftUI
// Use Firebase library to configure APIs FirebaseApp.configure()
迅速
// Use Firebase library to configure APIs FirebaseApp.configure()
Objective-C
// Use Firebase library to configure APIs [FIRApp configure];
- SwiftUI を使用している場合は、アプリケーション デリゲートを作成し、それを
UIApplicationDelegateAdaptor
またはNSApplicationDelegateAdaptor
を介してApp
構造体にアタッチする必要があります。アプリ デリゲート スウィズリングも無効にする必要があります。詳細については、 SwiftUI の説明を参照してください。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 の応答からの ID トークンをハッシュ化されていないナンスと共に使用して、Firebase 資格情報を取得します。迅速
// Initialize a Firebase credential. let idTokenString = AuthenticationToken.current?.tokenString let nonce = currentNonce let credential = OAuthProvider.credential(withProviderID: "facebook.com", idToken: idTokenString!, rawNonce: nonce)
Objective-C
// 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 // ... }
Objective-C
[[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セキュリティ ルールでは、サインインしているユーザーの一意のユーザー ID を
auth
変数から取得し、それを使用してユーザーがアクセスできるデータを制御できます。
認証プロバイダーの資格情報を既存のユーザー アカウントにリンクすることで、ユーザーが複数の認証プロバイダーを使用してアプリにサインインできるようにすることができます。
ユーザーをサインアウトするには、 signOut:
を呼び出します。
迅速
let firebaseAuth = Auth.auth() do { try firebaseAuth.signOut() } catch let signOutError as NSError { print("Error signing out: %@", signOutError) }
Objective-C
NSError *signOutError; BOOL status = [[FIRAuth auth] signOut:&signOutError]; if (!status) { NSLog(@"Error signing out: %@", signOutError); return; }
認証エラーの全範囲に対してエラー処理コードを追加することもできます。エラーの処理を参照してください。