Facebook ログインまたは Facebook 制限付きログインをアプリに統合することで、ユーザーは Facebook アカウントを使用して Firebase での認証を行えます。
始める前に
Swift Package Manager を使用して Firebase の依存関係をインストールし、管理します。
- Xcode でアプリのプロジェクトを開いたまま、[File] > [Add Packages] の順に移動します。
- プロンプトが表示されたら、Firebase Apple プラットフォーム SDK リポジトリを追加します。
- Firebase Authentication ライブラリを選択します。
- ターゲットのビルド設定の [Other Linker Flags] セクションに
-ObjC
フラグを追加します。 - 上記の作業が完了すると、Xcode は依存関係の解決とバックグラウンドでのダウンロードを自動的に開始します。
https://github.com/firebase/firebase-ios-sdk.git
次に、以下の構成手順を行います。
- Facebook for Developers サイトで、アプリのアプリ ID とアプリ シークレットを取得します。
- Facebook ログインを有効にします。
- Firebase コンソールで [Auth] セクションを開きます。
- [Sign-in method] タブで [Facebook] を有効にし、Facebook から取得した [アプリ ID] と [アプリ シークレット] を指定します。
- 次に、Facebook for Developers サイトの [Product Settings] > [Facebook Login] 構成にある Facebook アプリ設定ページで、OAuth リダイレクト URI(
my-app-12345.firebaseapp.com/__/auth/handler
など)が [OAuth redirect URIs] のうちの一つとしてリストされていることを確認します。
Facebook ログインを実装する
「クラシック」Facebook ログインを使用するには、次の手順を行います。または、Facebook 制限付きログインを使用することもできます。次のセクションをご覧ください。
- デベロッパー向けドキュメントに沿って Facebook ログインをアプリに統合します。
FBSDKLoginButton
オブジェクトを初期化するときに、ログイン イベントとログアウト イベントを受信するようにデリゲートを設定します。次に例を示します。Swift
let loginButton = FBSDKLoginButton() loginButton.delegate = self
Objective-C
FBSDKLoginButton *loginButton = [[FBSDKLoginButton alloc] init]; loginButton.delegate = self;
didCompleteWithResult:error:
を実装します。Swift
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、Authentication を使用するには、次のように指定します。SwiftUI
import SwiftUI import FirebaseCore import FirebaseFirestore import FirebaseAuth // ...
Swift
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()
Swift
// 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 認証情報と交換します。Swift
let credential = FacebookAuthProvider .credential(withAccessToken: AccessToken.current!.tokenString)
Objective-C
FIRAuthCredential *credential = [FIRFacebookAuthProvider credentialWithAccessToken:[FBSDKAccessToken currentAccessToken].tokenString];
Facebook 制限付きログインを実装する
「クラシック」Facebook ログインの代わりに Facebook 制限付きログインを使用するには、次の手順を行います。
- デベロッパー向けドキュメントに沿って Facebook 制限付きログインをアプリに統合します。
- ログイン リクエストごとに一意の文字列「ノンス」を生成します。ノンスは、取得した ID トークンが、アプリの認証リクエストへのレスポンスとして付与されたことを確認するために使用されます。この手順は、リプレイ攻撃の防止に重要です。
SecRandomCopyBytes(_:_:_)
を使用すると、暗号的に安全なノンスを生成できます。次に例を示します。Swift
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) }
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]; }
Swift
@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
に設定して、ノンスをアタッチします。次に例を示します。Swift
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:
を実装します。Swift
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、Authentication を使用するには、次のように指定します。SwiftUI
import SwiftUI import FirebaseCore import FirebaseFirestore import FirebaseAuth // ...
Swift
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()
Swift
// 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 認証情報を取得します。Swift
// 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 での認証を行います。
Swift
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:
を呼び出します。
Swift
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; }
さまざまな認証エラーに対応できるようにエラー処理コードを追加することもできます。エラーの処理をご覧ください。