skzhwのブログ

Go勉強中です。

Firebase Googleログイン初回ログインと継続ログインで処理の分岐

Firebaseの認証機能って便利だけど思うように動かないことありますよね。

今回やること

Googleログインを利用するユーザーがアプリ上でプロフィール変更を行なったにも関わらず、 次回ログインした時に初期設定になってしまうことを解消します。

前提として

  • Firestoreを少しでも触ったことがある方を対象としてます。
  • データはFirestoreのusers/{uid}の中に保存します。
  • React/ReduxToolkit + Typescriptで書いてます。

ユーザーデータの永続化における問題点

以下のコードでGoogleログインを行なった場合

export const signInGoogle = (): AppThunk => {
  return async (dispatch): Promise<void> => {
    return auth.signInWithPopup(provider).then((result) => {
      const user = result.user
      if (user) {
        const uid = user.uid
        const timestamp = FirebaseTimestamp.now()

        const userInitialData = {
          uid: uid,
          username: user.displayName,
          email: user.email,
          role: 'customer',
          updated_at: timestamp,
          created_at: timestamp,
        }
        usersRef
          .doc(uid)
          .set(userInitialData)
          .then(()=>dispatch.push('/'))
      }
    })
  }
}

写真のように登録される。
Authの画面 f:id:skzhw:20210815100444j:plain

Firestoreの画面 f:id:skzhw:20210815100449j:plain 一見問題ないですが、これだとアプリ側でプロフィールを変更した時でも常にuserInitialDataで定義した情報に書き換わってしまう。
例えばroleadminなどに変更してアプリ内で権限の制御を行う場合にログインするたびに規定の設定に戻ってしまう。

対処方法

FirebaseのAuth.currentUserにはmetadataというものがあって そこには作成日時ログイン日時が記録されていることがわかった。
そのため作成日時とログイン日時が一致している時は新規作成。
一致していなければ継続ログインということが判断できるため、
新規ログインの時のみ既存のデータをセットしておけば、アプリ側でユーザーがプロフィールを変更した場合でも、
変更された状態が維持されることがわかった。

export const signInGoogle = (): AppThunk => {
  return async (dispatch): Promise<void> => {
    return auth.signInWithPopup(provider).then((result) => {
      const user = result.user
      if (user) {
        const uid = user.uid
        const timestamp = FirebaseTimestamp.now()

            // 作成日時とログイン日時
                const creationTime = user?.metadata.creationTime
                const lastSignInTime = user?.metadata.lastSignInTime

      if (creationTime == lastSignInTime) {
        const userInitialData = {
          uid: uid,
          username: user.displayName,
          email: user.email,
          role: 'customer',
          updated_at: timestamp,
          created_at: timestamp,
        }
        usersRef
          .doc(uid)
          .set(userInitialData)
          .then(()=>dispatch.push('/'))
                }
            }.then(()=>dispatch.push('/'))
    })
  }
}

終わりに

他にベストな方法あれば教えていただければうれしいですが結構悩んだのちに. 最後はFirebaseのドキュメントに解決してもらうという。
ドキュメントの偉大を書いた方に感謝を伝えます。