Implementing Two-Factor Authentication (2FA): Claude Code 활용 가이드
implementing two-factor authentication (2fa): Claude Code 활용. 실용적인 코드 예시를 포함합니다.
二要素인증をClaude Code로 구현하기
アカウントの보안を強化する二要素인증(2FA)は、現在のWeb애플리케이션に欠かせない機能です。Claude Code를 활용하면 TOTPベースの2FA、QRコード표시、백업コードを含む完全な구현を구축할 수 있습니다。
TOTPの仕組み
TOTP(Time-based One-Time Password)は、共有シークレットと現在時刻から30秒ごとに6桁のコードを생성するアルゴリズムです。Google AuthenticatorやAuthyなどの앱で利用されています。
> TOTPベースの二要素인증를 구현해줘。
> QRコード생성、유효化フロー、로그イン時の검증、
> 백업コード10個の생성も含めて。
シークレット생성とQRコード
// src/lib/two-factor.ts
import { authenticator } from 'otplib';
import QRCode from 'qrcode';
import crypto from 'crypto';
import { prisma } from './prisma';
export class TwoFactorService {
// 2FA설정の開始(シークレットとQRコード생성)
async setup(userId: string) {
const secret = authenticator.generateSecret();
const user = await prisma.user.findUnique({ where: { id: userId } });
const otpauthUrl = authenticator.keyuri(
user!.email,
'MyApp',
secret
);
const qrCodeDataUrl = await QRCode.toDataURL(otpauthUrl);
// シークレットを一時저장(まだ유효化しない)
await prisma.user.update({
where: { id: userId },
data: { twoFactorSecret: secret, twoFactorEnabled: false },
});
return { qrCodeDataUrl, secret };
}
// コードを검증して2FAを유효化
async enable(userId: string, token: string) {
const user = await prisma.user.findUnique({ where: { id: userId } });
if (!user?.twoFactorSecret) throw new Error('2FAが設定されていません');
const isValid = authenticator.verify({
token,
secret: user.twoFactorSecret,
});
if (!isValid) throw new Error('無効なコードです');
// 백업コードを생성
const backupCodes = this.generateBackupCodes();
const hashedCodes = backupCodes.map((code) =>
crypto.createHash('sha256').update(code).digest('hex')
);
await prisma.user.update({
where: { id: userId },
data: {
twoFactorEnabled: true,
backupCodes: hashedCodes,
},
});
return { backupCodes }; // 사용자に표시(一度だけ)
}
// 로그イン時のコード검증
async verify(userId: string, token: string): Promise<boolean> {
const user = await prisma.user.findUnique({ where: { id: userId } });
if (!user?.twoFactorSecret) return false;
// TOTPコードで검증
const isValid = authenticator.verify({
token,
secret: user.twoFactorSecret,
});
if (isValid) return true;
// 백업コードで검증
const hashedToken = crypto.createHash('sha256').update(token).digest('hex');
const codeIndex = user.backupCodes.indexOf(hashedToken);
if (codeIndex !== -1) {
// 使用済みの백업コードをDelete
const updatedCodes = [...user.backupCodes];
updatedCodes.splice(codeIndex, 1);
await prisma.user.update({
where: { id: userId },
data: { backupCodes: updatedCodes },
});
return true;
}
return false;
}
private generateBackupCodes(count = 10): string[] {
return Array.from({ length: count }, () =>
crypto.randomBytes(4).toString('hex').toUpperCase()
);
}
}
2FA설정画面
// src/components/TwoFactorSetup.tsx
'use client';
import { useState } from 'react';
export function TwoFactorSetup() {
const [step, setStep] = useState<'idle' | 'qr' | 'verify' | 'backup'>('idle');
const [qrCode, setQrCode] = useState('');
const [token, setToken] = useState('');
const [backupCodes, setBackupCodes] = useState<string[]>([]);
const startSetup = async () => {
const res = await fetch('/api/user/2fa/setup', { method: 'POST' });
const data = await res.json();
setQrCode(data.qrCodeDataUrl);
setStep('qr');
};
const verifyAndEnable = async () => {
const res = await fetch('/api/user/2fa/enable', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token }),
});
const data = await res.json();
setBackupCodes(data.backupCodes);
setStep('backup');
};
if (step === 'idle') {
return (
<button onClick={startSetup} className="bg-blue-600 text-white px-6 py-2 rounded-lg">
二要素認証を設定する
</button>
);
}
if (step === 'qr') {
return (
<div className="space-y-4">
<p className="dark:text-gray-300">認証アプリでQRコードをスキャンしてください</p>
<img src={qrCode} alt="QRコード" className="mx-auto" />
<input
value={token}
onChange={(e) => setToken(e.target.value)}
placeholder="6桁のコードを入力"
className="w-full border rounded px-3 py-2"
maxLength={6}
/>
<button onClick={verifyAndEnable} className="bg-blue-600 text-white px-6 py-2 rounded-lg w-full">
確認して有効化
</button>
</div>
);
}
return (
<div className="space-y-4">
<h3 className="font-bold text-green-600">二要素認証が有効になりました</h3>
<p className="text-sm text-gray-600 dark:text-gray-400">
以下のバックアップコードを安全な場所に保存してください。各コードは一度だけ使えます。
</p>
<div className="grid grid-cols-2 gap-2 bg-gray-50 dark:bg-gray-800 p-4 rounded-lg font-mono text-sm">
{backupCodes.map((code, i) => (
<span key={i} className="dark:text-gray-300">{code}</span>
))}
</div>
</div>
);
}
関連글
인증全般에 대해서는인증機能の구현가이드、비밀번호関連は비밀번호리셋구현도 참고하세요.
TOTP의 사양은RFC 6238で定義されています。
무료 PDF: 5분 완성 Claude Code 치트시트
이메일 주소만 등록하시면 A4 한 장짜리 치트시트 PDF를 즉시 보내드립니다.
개인정보는 엄격하게 관리하며 스팸은 보내지 않습니다.
이 글을 작성한 사람
Masa
Claude Code를 적극 활용하는 엔지니어. 10개 언어, 2,000페이지 이상의 테크 미디어 claudecode-lab.com을 운영 중.
관련 글
Claude Code 다국어 글을 매일 발행하기 전에 확인할 7가지
누락된 언어, 깨진 CTA, 반영되지 않은 배포를 막기 위해 다국어 Claude Code 글을 매일 발행하기 전에 확인할 체크리스트입니다.
Codex Automations란? 잠자는 동안 AI가 콘텐츠 운영을 처리하게 하는 방법
Codex Automations로 트래픽 분석, 주제 선정, 글 작성, CTA 개선, 배포까지 자동화하는 실전 가이드.
Claude Code × GCP Cloud Functions 완전 가이드 | 서버리스 함수 초고속 개발
Claude Code로 GCP Cloud Functions를 효율화. HTTP/Pub/Sub/Firestore 트리거 구현부터 로컬 테스트·배포 자동화까지, Masa의 실무 경험을 토대로 실제 코드로 해설.