やりたいこと
- 個人ごとにユーザ管理したりするほどの要件ではない
- かといってサイトにアクセスできた人全員が使えてしまうのは困るので簡易ログインはほしい
- ログイン機能は自分では実装したくない
→「認証コード」を使ったログイン機能をFirebase Authenticationで実現したい
できたもの
4行で
- 匿名認証してUIDをゲット
- 1のUIDと入力された認証コードをCloud Functionsに投げる
- 認証コードが正しければ2のUIDにCustom claimを付与
- 以後のログインチェックはCustom claimで判定
フロー
実装
本記事はJSで実装していますが言語は特に重要ではないため、その他の言語でも同じように実装できると思います。要所を抜粋しているので単純にコピペしても動作しません。
フロントエンド
①〜④、⑧の部分です。
login.js
// 匿名認証でログインしてUIDを取得
const userCredential = await firebase.auth().signInAnonymously()
const uid = userCredential.user.uid
// 認証コードが正しければそのユーザーにカスタムクレイムを付与する関数を呼び出し
const authWithCode = firebase.functions().httpsCallable('authWithCode')
const result = await authWithCode({ uid, verifyCode: this.code })
// カスタムクレイム付与後、強制的にトークンをリフレッシュ
await firebase.auth().currentUser.getIdToken(true)
Vue.jsのrouterを使用する場合は以下のようにログイン判定ができます。
router.js
firebase.auth().onAuthStateChanged(async function (user) {
if (user) {
let getIdTokenResult = await user.getIdTokenResult()
if (getIdTokenResult.claims.codeVerified) {
// カスタムクレイムで認証成功したとき
next()
} else {
// 匿名認証はしているけどカスタムクレイムが付与されていないとき
next({
path: '/login',
query: { redirect: to.fullPath }
})
}
} else {
// 匿名認証すらしていないとき
next({
path: '/login',
query: { redirect: to.fullPath }
})
}
})
コード検証関数
⑤〜⑦の部分です。
codeVerify.js
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const VERIFY_CODE = '0112'
exports.authWithCode = functions.https.onCall(async (request, response) => {
// 設定された認証コードと送られてきた認証コードが一致するか判定
if(request.verifyCode === VERIFY_CODE) {
try{
// Custom claimを付与
await admin.auth().setCustomUserClaims(request.uid, {codeVerified: true})
return { status: 'ok', message: '認証しました。' }
}catch(error){
return { status: 'error', message: 'ログイン処理に失敗しました。' }
}
}else{
return { status: 'error', message: '認証コードが正しくありません。' }
}
});
Firestoreルール
データソースのルールはCustom claimで判定するようにするとコード認証したユーザーのみ操作が許可できます。
rule.js
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read: if request.auth.token.codeVerified == true;
allow write: if false;
}
}
}
参考
[公式]Custom claimについて
https://firebase.google.com/docs/auth/admin/custom-claims?hl=ja#best_practices_for_custom_claims
https://firebase.google.com/docs/auth/admin/custom-claims?hl=ja
Callable HTTPS Functionについて
https://miso-soup3.hateblo.jp/entry/2018/05/22/215453