💬

[Firebase v9] [Nuxt3] [LIFF]LINE アカウントでカスタム認証

2023/11/24に公開

⓪ログインしているユーザーを取得する

現在ログインしているユーザーの情報を取得する方法は2つある。

前提

まずは Firebase アプリを initializeApp() で初期化する。

  const config = useRuntimeConfig()
  const firebaseConfig = {
    apiKey: config.public.apiKey,
    authDomain: config.public.authDomain,
    projectId: config.public.projectId,
    appId: config.public.appId,
  }
  const app = initializeApp(firebaseConfig)
  const auth = getAuth(app)

初期化したFirebase アプリを引数に getAuth() で生成した auth オブジェクトに、auth 周りの全てが格納されている。

auth.currentUser

現在ログインしているユーザーの情報は単純に auth.currentUser で取得できる。

onAuthStateChanged(auth, (user) => ... )

auth オブジェクトを監視するオブザーバを設置して、現在ログインしているユーザー情報を取得することもできる。
トリガーはログイン/ログアウトのみ。ログインを感知したら情報の詰まった user が、ログアウトを感知したら null の user が返ってくる。

オブザーバは1つで十分なので、アプリ起動時に実行される plugin に処理を記述する。plugin はSSRだとサーバサイドとクライアントサイドで二回実行されてしまうため、.client というサフィックス付与でクライアント側のみの1回だけ実行。
アプリ内で2つ以上オブザーバを設置してしまう場合は、重複してしまうため、きちんとオブザーバを解除する必要がある。

const unsubscribe = onAuthStateChanged(auth, (user) => ... )
unsubscribe()

これだと「オブザーバ」という役割を果たしてないし、使用するケースは今のところ思いつかない。多分コンポーネント単位で監視したいときかなと思う。

プロフィール情報を更新したとき

updateProfile(auth.currentUser, updateData) でプロフィールを更新できるが、onAuthStateChanged() はこの変更は感知しない。そのためこのままだと firebase上で更新されただけなので、auth.currentUser.reload() でアプリ内の auth オブジェクトも強制的に最新にする。

updateProfile(auth.currentUser, updateData).then(async () => {
  await auth.currentUser.reload()
  // その他処理
})

①未ログインの場合、LINE アカウントを使用して Firebase にログインする

具体的には、LINEアカウントの uid を使って Firebase のカスタム認証を行っていく。

頭の整理

Firebase カスタム認証を実行するためには、firebase-admin の createCustomToken() で発行するカスタムトークンが必要。
そのカスタムトークンを発行するためには、LINE アカウントの uid の文字列が必要。
LINE アカウントの uid を取得するためには、LINE Platform からプロフィール情報の取得が必要。
LINE Platform からプロフィール情報を取得するためには、ログインしている LINE アカウントによって LINE SDK で発行されたアクセストークンが必要。
LIINE SDK で発行されたアクセストークンは、LIFFアプリの初期化時に取得できる。

主な関数とフロー

参考: アプリとサーバーの間で安全なログインプロセスを構築する

  1. LIFFの初期化 liff.init({ liffId })
  2. LINE SDK からアクセストークンを取得 liff.getAccessToken()
  3. アクセストークンをサーバに送り、LINE Platform と通信してそのトークンが安全で有効なものなのかを検証(ステータス、チャンネルID、期限、の3点チェック)
  4. 有効なら、そのトークンを使って LINE Platform からユーザーのプロフィール情報(uid、表示名, 画像URL, 一言)を取得
  5. 取得した uid を引数に firebase-admin の createCustomToken() でカスタムトークンを生成
  6. 取得したプロフィール情報と発行したカスタムトークンをクライアントに返し、カスタムトークンを引数として signInWithCustomToken() で Firebase カスタム認証をクライアント上で行う
  7. ⓪のオブザーバが感知する

脆弱性のある危険なフロー

  1. LIFFの初期化 liff.init({ liffId })
  2. LINE SDK からアクセストークンを取得 liff.getAccessToken()
  3. アクセストークンを用いて LINE Platform からプロフィール情報を取得する
  4. その取得した uid をサーバーに渡す
  5. 渡された uid を用いて createCustomToken() を実行 <- この渡された uid は信用できない!

...つまり、クライアント上で取得したプロフィール情報は偽装できてしまうのでサーバには送るな、サーバサイドでプロフィール情報を取得せよ、取得するにはクライアント側で発行したアクセストークンをサーバに送って用いてね、使う前にそれが信用できるものか検証してから使ってね、ということ。

Discussion