iOS で Google ログインを使用して認証する

Google ログインをアプリに統合して、ユーザーが Firebase での認証に Google アカウントを使用できるようにします。

準備

  1. Firebase を iOS プロジェクトに追加しますPodfile に次のポッドを含めます。
    pod 'Firebase/Auth'
    pod 'GoogleSignIn'
    
  2. アプリを Firebase プロジェクトに接続していない場合は、Firebase コンソールで接続します。
  3. Firebase コンソールで Google ログインを有効にします。
    1. Firebase コンソールで [Authentication] セクションを開きます。
    2. [ログイン方法] タブで [Google] を有効にし、[保存] をクリックします。

1. 必須ヘッダー ファイルをインポートする

まず Firebase SDK と Google ログイン SDK のヘッダー ファイルをアプリにインポートします。

Swift

アプリのデリゲートで、次のヘッダー ファイルをインポートします。

import Firebase
import GoogleSignIn

ログインビューのビュー コントローラで、次のヘッダー ファイルをインポートします。

import Firebase
import GoogleSignIn

Objective-C

アプリのデリゲートで、次のヘッダー ファイルをインポートします。

@import Firebase;
@import GoogleSignIn;

ログインビューのビュー コントローラで、次のヘッダー ファイルをインポートします。

@import Firebase;
@import GoogleSignIn;

2. Google ログインを実装する

Google ログインを実装する手順は次のとおりです。iOS で Google ログインを使用する方法について詳しくは、Google ログインに関するデベロッパー向けドキュメントをご覧ください。

  1. Xcode プロジェクトにカスタム URL スキームを追加します。
    1. プロジェクトの設定を開きます(左側のツリービューでプロジェクト名をダブルクリックします)。[ターゲット] セクションでアプリを選択し、[情報] タブを開いて [URL タイプ] セクションを展開します。
    2. [+] ボタンをクリックし、反転クライアント ID の URL スキームを追加します。この値を確認するには、GoogleService-Info.plist 構成ファイルを開いて REVERSED_CLIENT_ID キーを探します。見つかったキーの値をコピーし、設定ページの [URL スキーム] ボックスに貼り付けます。その他の入力欄は空白にしておきます。

      完了すると、設定は次のようになります(ただし、値はアプリケーションによって異なります)。

  2. アプリのデリゲートによって GIDSignInDelegate プロトコルを実装することを宣言します。

    Swift

    AppDelegate.swift 内:
    class AppDelegate: UIResponder, UIApplicationDelegate, GIDSignInDelegate {
    

    Objective-C

    AppDelegate.h 内:
    @interface AppDelegate : UIResponder<UIApplicationDelegate, GIDSignInDelegate>
    
  3. アプリのデリゲートの application:didFinishLaunchingWithOptions: メソッドで、FirebaseApp オブジェクトを構成し、ログインのデリゲートを設定します。

    Swift

    // Use Firebase library to configure APIs
    FirebaseApp.configure()
    
    GIDSignIn.sharedInstance().clientID = FirebaseApp.app()?.options.clientID
    GIDSignIn.sharedInstance().delegate = self
    

    Objective-C

    // Use Firebase library to configure APIs
    [FIRApp configure];
    
    [GIDSignIn sharedInstance].clientID = [FIRApp defaultApp].options.clientID;
    [GIDSignIn sharedInstance].delegate = self;
    
  4. アプリのデリゲートに application:openURL:options: メソッドを実装します。このメソッドは GIDSignIn インスタンスの handleURL メソッドを呼び出します。これによって、認証プロセスの最後にアプリが受け取る URL が正しく処理されます。

    Swift

    @available(iOS 9.0, *)
    func application(_ application: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any])
      -> Bool {
      return GIDSignIn.sharedInstance().handle(url)
    }
    

    Objective-C

    - (BOOL)application:(nonnull UIApplication *)application
                openURL:(nonnull NSURL *)url
                options:(nonnull NSDictionary<NSString *, id> *)options {
      return [[GIDSignIn sharedInstance] handleURL:url];
    }
    

    iOS 8 以前で実行されるアプリの場合は、サポートが終了した application:openURL:sourceApplication:annotation: メソッドも実装します。

    Swift

    func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
        return GIDSignIn.sharedInstance().handle(url)
    }
    

    Objective-C

    - (BOOL)application:(UIApplication *)application
                openURL:(NSURL *)url
      sourceApplication:(NSString *)sourceApplication
             annotation:(id)annotation {
      return [[GIDSignIn sharedInstance] handleURL:url];
    }
    
  5. アプリのデリゲートで、ログイン プロセスを処理する GIDSignInDelegate プロトコルを実装するために次のメソッドを定義します。

    Objective-C

    - (void)signIn:(GIDSignIn *)signIn
    didSignInForUser:(GIDGoogleUser *)user
         withError:(NSError *)error {
      // ...
      if (error == nil) {
        GIDAuthentication *authentication = user.authentication;
        FIRAuthCredential *credential =
        [FIRGoogleAuthProvider credentialWithIDToken:authentication.idToken
                                         accessToken:authentication.accessToken];
        // ...
      } else {
        // ...
      }
    }
    
    - (void)signIn:(GIDSignIn *)signIn
    didDisconnectWithUser:(GIDGoogleUser *)user
         withError:(NSError *)error {
      // Perform any operations when the user disconnects from app here.
      // ...
    }
    

    Swift

    func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error?) {
      // ...
      if let error = error {
        // ...
        return
      }
    
      guard let authentication = user.authentication else { return }
      let credential = GoogleAuthProvider.credential(withIDToken: authentication.idToken,
                                                        accessToken: authentication.accessToken)
      // ...
    }
    
    func sign(_ signIn: GIDSignIn!, didDisconnectWith user: GIDGoogleUser!, withError error: Error!) {
        // Perform any operations when the user disconnects from app here.
        // ...
    }
    
  6. ビュー コントローラで、viewDidLoad メソッドをオーバーライドして GIDSignIn オブジェクトのプレゼンター ビュー コントローラを設定します。さらに、可能な場合はサイレント ログインするようにします(省略可)。

    Objective-C

    [GIDSignIn sharedInstance].presentingViewController = self;
    [[GIDSignIn sharedInstance] signIn];
    

    Swift

    GIDSignIn.sharedInstance()?.presentingViewController = self
    GIDSignIn.sharedInstance().signIn()
    
  7. GIDSignInButton をストーリーボードまたは XIB ファイルに追加するか、プログラムによって初期化します。このボタンをストーリーボードまたは XIB ファイルに追加するには、ビューを追加して、そのカスタムクラスを GIDSignInButton に設定します。
  8. 省略可: このボタンをカスタマイズする方法は次のとおりです。

    Swift

    1. ビュー コントローラで、ログインボタンをプロパティとして宣言します。
      @IBOutlet weak var signInButton: GIDSignInButton!
    2. 宣言した signInButton プロパティにボタンを接続します。
    3. GIDSignInButton オブジェクトのプロパティを設定してボタンをカスタマイズします。

    Objective-C

    1. ビュー コントローラのヘッダー ファイルで、ログインボタンをプロパティとして宣言します。
      @property(weak, nonatomic) IBOutlet GIDSignInButton *signInButton;
    2. 宣言した signInButton プロパティにボタンを接続します。
    3. GIDSignInButton オブジェクトのプロパティを設定してボタンをカスタマイズします。

3. Firebase で認証する

signIn:didSignInForUser:withError: メソッドで、Google ID トークンと Google アクセス トークンを GIDAuthentication オブジェクトから取得して、Firebase 認証情報と交換します。

Swift

func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error?) {
  // ...
  if let error = error {
    // ...
    return
  }

  guard let authentication = user.authentication else { return }
  let credential = GoogleAuthProvider.credential(withIDToken: authentication.idToken,
                                                    accessToken: authentication.accessToken)
  // ...
}

Objective-C

- (void)signIn:(GIDSignIn *)signIn
didSignInForUser:(GIDGoogleUser *)user
     withError:(NSError *)error {
  // ...
  if (error == nil) {
    GIDAuthentication *authentication = user.authentication;
    FIRAuthCredential *credential =
    [FIRGoogleAuthProvider credentialWithIDToken:authentication.idToken
                                     accessToken:authentication.accessToken];
    // ...
  } else {
    // ...
  }
}

最後に、認証情報を使用して 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 プロジェクトの一部として保存され、ユーザーのログイン方法にかかわらず、プロジェクトのすべてのアプリでユーザーを識別するために使用できます。

  • アプリでは、FIRUser オブジェクトからユーザーの基本的なプロフィール情報を取得できます。ユーザーの管理についての記事をご覧ください。

  • 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;
}

さまざまな認証エラーに対応できるようにエラー処理コードを追加することもできます。エラーの処理をご覧ください。