Mengimplementasikan Password Reset dengan Claude Code
Learn about implementing password reset using Claude Code. Includes practical code examples.
パスワードリセット機能 dengan Claude Code: implementasi
パスワードリセット ほぼすべて Webaplikasi diperlukanな機能 す 、セキュリティ上 落 dan し穴 juga 多い領域.Claude Code 使えば、token amanなgenerate・verifikasi、レート制限、メールpengiriman 含むrobustなimplementasi pembangunan bisa dilakukan.
全体像 リセットフロー
- pengguna メールアドレス input
- server リセットtoken generateしメールpengiriman
- pengguna メール内 リンク クリック
- 新しいパスワード input pembaruan
> セキュアなパスワードリセット機能 implementasikan.
> token efektif期限 1時間、penggunaan 1回 み。
> レート制限 dan メールpengiriman juga 含めて。
tokengenerateとメールpengiriman
// src/services/password-reset.ts
import crypto from 'crypto';
import bcrypt from 'bcryptjs';
import { prisma } from '@/lib/prisma';
import { sendEmail } from '@/lib/email';
export class PasswordResetService {
private static TOKEN_EXPIRY_HOURS = 1;
async requestReset(email: string) {
const user = await prisma.user.findUnique({ where: { email } });
// pengguna 存在しなくて 同じresponse 返す(情報漏洩防止)
if (!user) return { success: true };
// レート制限: 同一メールへ pengiriman 5分 1回ま
const recentRequest = await prisma.passwordReset.findFirst({
where: {
userId: user.id,
createdAt: { gt: new Date(Date.now() - 5 * 60 * 1000) },
},
});
if (recentRequest) return { success: true };
// セキュアなtokengenerate
const token = crypto.randomBytes(32).toString('hex');
const hashedToken = crypto.createHash('sha256').update(token).digest('hex');
// 古いtoken Delete
await prisma.passwordReset.deleteMany({ where: { userId: user.id } });
// 新しいtoken penyimpanan
await prisma.passwordReset.create({
data: {
userId: user.id,
token: hashedToken,
expiresAt: new Date(
Date.now() + this.constructor.TOKEN_EXPIRY_HOURS * 60 * 60 * 1000
),
},
});
// リセットメールpengiriman
const resetUrl = `${process.env.APP_URL}/reset-password?token=${token}`;
await sendEmail({
to: email,
subject: 'パスワードリセット ご案内',
html: `
<h2>パスワードリセット</h2>
<p>以下のリンクをクリックしてパスワードをリセットしてください。</p>
<a href="${resetUrl}" style="display:inline-block;background:#3B82F6;color:#fff;padding:12px 24px;border-radius:8px;text-decoration:none;">
パスワードをリセット
</a>
<p style="color:#666;font-size:14px;margin-top:16px;">
このリンクは1時間で有効期限が切れます。<br />
心当たりがない場合は、このメールを無視してください。
</p>
`,
});
return { success: true };
}
async resetPassword(token: string, newPassword: string) {
const hashedToken = crypto.createHash('sha256').update(token).digest('hex');
const resetRecord = await prisma.passwordReset.findFirst({
where: {
token: hashedToken,
expiresAt: { gt: new Date() },
used: false,
},
});
if (!resetRecord) {
throw new Error('無効また 期限切れ token す');
}
// パスワード validasi
if (newPassword.length < 8) {
throw new Error('パスワード 8文字以上 adadiperlukan あります');
}
const hashedPassword = await bcrypt.hash(newPassword, 12);
// トランザクション パスワードpembaruan dan token無効化
await prisma.$transaction([
prisma.user.update({
where: { id: resetRecord.userId },
data: { password: hashedPassword },
}),
prisma.passwordReset.update({
where: { id: resetRecord.id },
data: { used: true },
}),
]);
return { success: true };
}
}
UI リセットform
// src/app/(auth)/reset-password/page.tsx
'use client';
import { useState } from 'react';
import { useSearchParams } from 'next/navigation';
export default function ResetPasswordPage() {
const searchParams = useSearchParams();
const token = searchParams.get('token');
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [status, setStatus] = useState<'idle' | 'success' | 'error'>('idle');
const [error, setError] = useState('');
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (password !== confirmPassword) {
setError('パスワード 一致しません');
return;
}
try {
const res = await fetch('/api/auth/reset-password', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token, password }),
});
if (!res.ok) throw new Error((await res.json()).error);
setStatus('success');
} catch (err: any) {
setError(err.message);
setStatus('error');
}
};
if (status === 'success') {
return (
<div className="min-h-screen flex items-center justify-center">
<div className="text-center">
<h2 className="text-2xl font-bold mb-4">パスワードを更新しました</h2>
<a href="/login" className="text-blue-600 hover:underline">ログインページへ</a>
</div>
</div>
);
}
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50">
<form onSubmit={handleSubmit} className="bg-white p-8 rounded-xl shadow-lg w-full max-w-md">
<h1 className="text-2xl font-bold mb-6">新しいパスワード</h1>
{error && <p className="text-red-500 text-sm mb-4">{error}</p>}
<div className="space-y-4">
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="新しいパスワード(8文字以上)"
className="w-full border rounded-lg px-4 py-3"
minLength={8}
required
/>
<input
type="password"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
placeholder="パスワード konfirmasi"
className="w-full border rounded-lg px-4 py-3"
required
/>
<button type="submit" className="w-full bg-blue-600 text-white py-3 rounded-lg font-medium">
パスワードを更新
</button>
</div>
</form>
</div>
);
}
Checklist Keamanan
- token 暗号学的 amanな乱数 generate
- DB ハッシュ化 token penyimpanan
- token efektif期限 設ける(1時間程度)
- token 1回penggunaan後 無効化
- pengguna 存在有無 応答 漏らさ tidak
Artikel Terkait
認証全般 設計 認証機能 implementasi、二elemen認証 dan 組み合わせ 二elemen認証 implementasi silakan lihat.
メールpengiriman Resend(resend.com) シンプル おすすめ.
Related Posts
Cara Mempercepat Side Project dengan Claude Code [Dengan Contoh]
Pelajari cara mempercepat project development personal secara drastis menggunakan Claude Code. Dilengkapi contoh nyata dan workflow praktis dari ide hingga deployment.
Cara Mengotomatisasi Refactoring dengan Claude Code
Pelajari cara mengotomatisasi code refactoring secara efisien menggunakan Claude Code. Dilengkapi prompt praktis dan pola refactoring konkret untuk project nyata.
Panduan Lengkap Konfigurasi CORS dengan Claude Code
Pelajari tentang panduan lengkap konfigurasi CORS menggunakan Claude Code. Dilengkapi tips praktis dan contoh kode.