firebase/auth
は認証周りの機能を提供してくれるSDKです。今回はそれを用いて複数のプロバイダー(GoogleやGithub、Twitterなど)の認証情報を一人のユーザーと結びつける方法の紹介です。
具体的なケース
signInWithPopup
という関数が提供されていて、アプリケーションとプロバイダーを引数に渡すことでユーザー認証が行えるようになっています。以下のコードはGoogleアカウントを用いた認証の例です。
import { getAuth, signInWithPopup, GoogleAuthProvider } from 'firebase/auth'
// appはinitializeAppを用いてconfigファイルを元に生成したもの
import { app } from '../utils/firebase/client'
const provider = new GoogleAuthProvider()
const auth = getAuth(app)
// 認証を行う処理
const handleSignIn = () => {
signInWithPopup(auth, provider).then(/* ... */)
}
この時点で認証を試してみると、問題なく動きます。さてここに、GitHubアカウントを用いた認証を追加します。引数にProviderを渡せるようにして、Google同様にインスタンスを初期化しておきます。
import { getAuth, signInWithPopup, GoogleAuthProvider, GithubAuthProvider } from 'firebase/auth'
const googleProvider = new GoogleAuthProvider()
const githubProvider = new GithubAuthProvider()
const handleSignIn = (provider: AuthProvider) => {
signInWithPopup(auth, provider).then(/* ... */)
}
この状態でGitHubでの認証を試みると次のようなエラーが発生します。
auth/account-exists-with-different-credential
一旦エラーハンドリングする
ハンドラでエラーの内容を見てみます。エラーはオブジェクト形式で渡され、code
プロパティにエラーを示す文章が入います。内容は先程と同じ auth/account-exists-with-different-credential
が格納されています。
const handleSignIn = (provider: AuthProvider) => {
signInWithPopup(auth, provider)
.then(/* ... */)
.catch(error => {
console.log(error.code)
})
}
エラーから認証情報を生成する
OAuthProvider
というクラスを用いることでエラーからcredentialを生成できます。失敗した場合はnullが返ってきます。とりあえず生成して早期リターンさせておきます。(エラーが正しい場合は失敗する可能性はないのですが、コンパイラが警告を出してくる場合があるので正しく型とガード節を書いたほうが良いです。)
const credential: AuthCredential | null = OAuthProvider.credentialFromError(error)
if (credential === null) return
次に fetchSignInMethodsForEmail
という関数を用いて、メールアドレスから認証情報を取得します。メールアドレスは customData
プロパティから取得できます。
const email = error.customData.email
fetchSignInMethodsForEmail(auth, email).then(/* ... */)
resolveされたら、メールアドレスが使用しているメソッドが配列形式で得られます。今回のケースの場合、先にGoogle認証を行ってからGitHubでの認証を試みているので、google.com
が出力されるはずです。
fetchSignInMethodsForEmail(auth, email).then((methods: string[]) => {
console.log(methods[0]) // 認証に用いた方式を取り出せる
})
次に、得られた認証方式の文字列からプロバイダーを得る関数を定義してあげます。ここでreturnしているものは最初に初期化したインスタンスです。
const getProviderById = (id: string) => {
switch (id) {
default: {
return null
}
case 'google.com': {
return googleProvider
}
}
}
あとは linkWithCredential
関数を用いてこれらの情報をリンクさせ、同一ユーザーであることをFirebaseに教えてあげます。
.catch((error) => {
if (error.code === 'auth/account-exists-with-different-credential') {
const email = error.customData.email
const credential: AuthCredential | null = OAuthProvider.credentialFromError(error)
if (credential === null) return
fetchSignInMethodsForEmail(auth, email).then((methods: string[]) => {
const provider = getProviderById(methods[0])
if (provider === null) return
signInWithPopup(auth, provider).then(result => {
// 紐付けを行い、以降ログイン時はここまでのフローがスキップできる
linkWithCredential(result.user, credential)
})
})
}
})
Top comments (0)