two-factor-authentication-best-practices について
Better Auth に二要素認証を追加する開発者向けの実装ガイドです。TOTP、メールまたはSMSのOTP、バックアップコード、信頼済みデバイスとセキュリティ設定を扱います。
主な機能
- 01TOTP認証アプリの設定
- 02メールまたはSMSのOTP
- 03バックアップコードの生成と復旧
- 04信頼済みデバイスの管理
- 05コード保存とレート制限
活用シーン
- 01パスワード認証に追加確認を導入
- 02バックアップコードによるアカウント復旧
- 03OTPの送信方法と保存方法の設定
- 04信頼済みデバイスでのサインイン管理
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ better-auth/ skills --skill two-factor-authentication-best-practicesインストール後、Claude Code で/two-factor-authentication-best-practicesを入力して読み込まれたか確認します。
詳細
セットアップ
issuerを指定して、サーバー設定にtwoFactor()プラグインを追加します- クライアント設定に
twoFactorClient()プラグインを追加します npx auth@latest migrateを実行します(組み込みアダプター)。Drizzle/Prisma の場合は generate と push を実行します- 確認します:ユーザーテーブルに
twoFactorSecretカラムが存在することを確認します
import { betterAuth } from "better-auth";
import { twoFactor } from "better-auth/plugins";
export const auth = betterAuth({
appName: "My App",
plugins: [
twoFactor({
issuer: "My App",
}),
],
});
クライアント側のセットアップ
import { createAuthClient } from "better-auth/client";
import { twoFactorClient } from "better-auth/client/plugins";
export const authClient = createAuthClient({
plugins: [
twoFactorClient({
onTwoFactorRedirect() {
window.location.href = "/2fa";
},
}),
],
});
ユーザーの 2FA を有効にする
パスワードの確認が必要です。TOTP URI(QR コード用)とバックアップコードが返されます。
const enable2FA = async (password: string) => {
const { data, error } = await authClient.twoFactor.enable({
password,
});
if (data) {
// data.totpURI — generate a QR code from this
// data.backupCodes — display to user
}
};
最初の TOTP 検証が成功するまで、twoFactorEnabled は true に設定されません。skipVerificationOnEnable: true でこの動作を上書きできます(推奨されません)。
TOTP(認証アプリ)
QR コードの表示
import QRCode from "react-qr-code";
const TotpSetup = ({ totpURI }: { totpURI: string }) => {
return <QRCode value={totpURI} />;
};
TOTP コードの検証
現在の時間の前後 1 期間のコードを受け付けます:
const verifyTotp = async (code: string) => {
const { data, error } = await authClient.twoFactor.verifyTotp({
code,
trustDevice: true,
});
};
TOTP の設定オプション
twoFactor({
totpOptions: {
digits: 6, // 6 or 8 digits (default: 6)
period: 30, // Code validity period in seconds (default: 30)
},
});
OTP(メール/SMS)
OTP 配信の設定
import { betterAuth } from "better-auth";
import { twoFactor } from "better-auth/plugins";
import { sendEmail } from "./email";
export const auth = betterAuth({
plugins: [
twoFactor({
otpOptions: {
sendOTP: async ({ user, otp }, ctx) => {
await sendEmail({
to: user.email,
subject: "Your verification code",
text: `Your code is: ${otp}`,
});
},
period: 5, // Code validity in minutes (default: 3)
digits: 6, // Number of digits (default: 6)
allowedAttempts: 5, // Max verification attempts (default: 5)
},
}),
],
});
OTP の送信と検証
送信:authClient.twoFactor.sendOtp()。検証:authClient.twoFactor.verifyOtp({ code, trustDevice: true })。
OTP の保存時のセキュリティ
OTP コードをデータベースに保存する方法を設定します:
twoFactor({
otpOptions: {
storeOTP: "encrypted", // Options: "plain", "encrypted", "hashed"
},
});
カスタム暗号化の場合:
twoFactor({
otpOptions: {
storeOTP: {
encrypt: async (token) => myEncrypt(token),
decrypt: async (token) => myDecrypt(token),
},
},
});
バックアップコード
2FA を有効にすると自動的に生成されます。各コードは 1 回のみ使用できます。
バックアップコードの表示
const BackupCodes = ({ codes }: { codes: string[] }) => {
return (
<div>
<p>Save these codes in a secure location:</p>
<ul>
{codes.map((code, i) => (
<li key={i}>{code}</li>
))}
</ul>
</div>
);
};
バックアップコードの再生成
以前のコードはすべて無効になります:
const regenerateBackupCodes = async (password: string) => {
const { data, error } = await authClient.twoFactor.generateBackupCodes({
password,
});
// data.backupCodes contains the new codes
};
アカウント復旧でのバックアップコードの使用
const verifyBackupCode = async (code: string) => {
const { data, error } = await authClient.twoFactor.verifyBackupCode({
code,
trustDevice: true,
});
};
バックアップコードの設定
twoFactor({
backupCodeOptions: {
amount: 10, // Number of codes to generate (default: 10)
length: 10, // Length of each code (default: 10)
storeBackupCodes: "encrypted", // Options: "plain", "encrypted"
},
});
サインイン時の 2FA の処理
2FA が必要な場合、レスポンスに twoFactorRedirect: true が含まれます:
サインインのフロー
signIn.email({ email, password })を呼び出しますonSuccessでcontext.data.twoFactorRedirectを確認しますtrueの場合、/2fa検証ページにリダイレクトします- TOTP、OTP、またはバックアップコードで検証します
- 検証が成功するとセッション Cookie が作成されます
const signIn = async (email: string, password: string) => {
const { data, error } = await authClient.signIn.email(
{ email, password },
{
onSuccess(context) {
if (context.data.twoFactorRedirect) {
window.location.href = "/2fa";
}
},
}
);
};
サーバー側:auth.api.signInEmail を使用する場合は、"twoFactorRedirect" in response を確認します。
信頼済みデバイス
検証時に trustDevice: true を渡します。デフォルトの信頼期間は 30 日(trustDeviceMaxAge)です。サインインのたびに更新されます。
セキュリティに関する考慮事項
セッション管理
フロー:認証情報 → セッションを削除 → 一時的な 2FA Cookie(デフォルト 10 分)→ 検証 → セッションを作成。
twoFactor({
twoFactorCookieMaxAge: 600, // 10 minutes in seconds (default)
});
レート制限
組み込みの制限:すべての 2FA エンドポイントで 10 秒あたり 3 リクエスト。OTP には追加の試行回数制限があります:
twoFactor({
otpOptions: {
allowedAttempts: 5, // Max attempts per OTP code (default: 5)
},
});
保存時の暗号化
TOTP シークレット:認証シークレットで暗号化されます。バックアップコード:デフォルトで暗号化されます。OTP:設定可能("plain"、"encrypted"、"hashed")。検証には定時間比較を使用します。
2FA を有効にできるのは、認証情報(メール/パスワード)アカウントのみです。
2FA を無効にする
パスワードの確認が必要です。信頼済みデバイスのレコードを取り消します:
const disable2FA = async (password: string) => {
const { data, error } = await authClient.twoFactor.disable({
password,
});
};
完全な設定例
import { betterAuth } from "better-auth";
import { twoFactor } from "better-auth/plugins";
import { sendEmail } from "./email";
export const auth = betterAuth({
appName: "My App",
plugins: [
twoFactor({
// TOTP settings
issuer: "My App",
totpOptions: {
digits: 6,
period: 30,
},
// OTP settings
otpOptions: {
sendOTP: async ({ user, otp }) => {
await sendEmail({
to: user.email,
subject: "Your verification code",
text: `Your code is: ${otp}`,
});
},
period: 5,
allowedAttempts: 5,
storeOTP: "encrypted",
},
// Backup code settings
backupCodeOptions: {
amount: 10,
length: 10,
storeBackupCodes: "encrypted",
},
// Session settings
twoFactorCookieMaxAge: 600, // 10 minutes
trustDeviceMaxAge: 30 * 24 * 60 * 60, // 30 days
}),
],
});