🎉

メールをつかったワンタイムパスワード認証+Firebase Authenticationの実装方法メモ

2024/01/03に公開

※ワンタイムパスワードって書くの面倒なので以下OTPと表記
※OTP認証=ユーザーが登録メールアドレスでOTPを受け取り、OTPを入力してサインインするやつ

メールアドレスをつかった認証方式についてFirebase Authenticationが標準でサポートしているのはメールアドレス+パスワード認証とメールリンク認証のみで、OTP認証は提供されてない。

Auth0やSupabaseでは標準提供されているが、OTP認証のためだけに移行するモチベは湧かなかったので自前で実装してみた。その時のメモ。

やることリスト

  1. クライアントでメアド入力を受け付ける
  2. サーバーでOTPを発行して、メールする
  3. クライアントでOTPの入力を受け付ける
  4. サーバーでOTPの入力値を検証して、トークンを取得する
  5. クライアントでトークンを使用してサインインする

1~3

特に語ることがないので省略

4. サーバーでOTPの入力値を検証して、トークンを取得する

以下、雰囲気のコード

export const handler = async (req, res) => {
  if (req.method === 'POST') {
    // ワンタイムパスワードを認証する
    try {
      const { userId, oneTimePassword } = req.body

      // ワンタイムパスワードを検索するコード
      const matchedOneTimePassword = 略

      // ワンタイムパスワードが合致しなかった場合はカスタムエラーを早期リターン
      if (!matchedOneTimePassword) {
        略
      }

      // ワンタイムパスワードが合致した場合
        // ワンタイムパスワードをverifiedにする処理
	略
	
        // カスタムトークンを取得する
        const customToken = await admin.auth().createCustomToken(userId)
        return res.status(200).send({ customToken })
      }
    } catch (error: any) {
      return res.status(500).json(error)
    }
  } else {
    res.setHeader('Allow', 'POST')
    return res.status(405).end('Method Not Allowed')
  }
}

firebaseではサーバーサイドでカスタムトークンを作成することができるのでこれを使う。
https://firebase.google.com/docs/auth/admin/create-custom-tokens?hl=ja

まずOTPがあっているかどうかの検証を行い、検証結果が問題なければトークンを発行する。

5. クライアントでトークンを使用してサインインする

以下、雰囲気のコード

export const firebaseCustomTokenSignIn = async (data) => {
  try {
    const { customToken } = data
    const userCredential = await signInWithCustomToken(firebaseAuth(), customToken)

    const user = userCredential.user
    return user
  } catch (error) {
    console.log(error)
    throw error
  }
}

firebase authにはsignInWithCustomTokenという、名前の通りカスタムトークンを使ってサインインするモジュールがあるので、これを使う。

https://firebase.google.com/docs/auth/web/custom-auth?hl=ja

完成

こんな感じで割りと簡単に実装できた。30分くらいで実装できるし、これならたしかに認証標準で提供されてなくてもいい気がする。

OTPのパスワードを6桁にしたい会社もあれば8桁にしたい会社もあるし、数字にしたい会社もあれば英数字にしたい会社もあるだろうから、Google的には「カスタムトークン認証用意するからその前段は好きにしてくれ」っていうことなのかもしれない。

Discussion