feat: 비밀번호 재설정 플로우 (이메일 링크 기반)
백엔드: - User 엔티티에 passwordResetTokenHash/passwordResetExpiresAt(select:false) 추가 - UserService: setPasswordResetToken/findByPasswordResetTokenHash/updatePassword - MailService: sendPasswordResetEmail (dev=로그 출력, prod=수신자만) - AuthService: requestPasswordReset(존재 비노출·로컬계정만·1시간 토큰), resetPassword(토큰 해시 검증→새 비번 저장→전 세션 폐기) - POST /auth/forgot-password(3/분)·/auth/reset-password(5/분), 가입과 동일 비번 규칙 - OAuth 콜백의 잔여 /repos 리다이렉트를 /projects 로 교정(리팩터 누락분) 프론트: - useAuth: forgotPassword/resetPassword - ForgotPasswordPage(/forgot-password), ResetPasswordPage(/reset-password, 토큰=쿼리) - LoginPage '비밀번호 찾기' 링크 연결 + 재설정 완료(?reset=1) 배너 런타임 검증: 재설정→새 비번 로그인 200·구 비번 거부·토큰 재사용 거부·약한 비번 VAL_001· 없는 이메일 동일응답(비노출). 백엔드 build/lint 0·14 tests, 프론트 type-check/lint/build 0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -20,6 +20,16 @@ export function useAuth() {
|
||||
await api.post('/auth/resend-verification', { email })
|
||||
}
|
||||
|
||||
// 비밀번호 재설정 요청 — 재설정 메일 발송(존재 여부 비노출)
|
||||
async function forgotPassword(email: string): Promise<void> {
|
||||
await api.post('/auth/forgot-password', { email })
|
||||
}
|
||||
|
||||
// 비밀번호 재설정 — 메일 링크의 토큰 + 새 비밀번호
|
||||
async function resetPassword(token: string, password: string): Promise<void> {
|
||||
await api.post('/auth/reset-password', { token, password })
|
||||
}
|
||||
|
||||
// 로그인
|
||||
async function login(payload: LoginPayload): Promise<AuthUser> {
|
||||
return (await api.post('/auth/login', payload)) as unknown as AuthUser
|
||||
@@ -36,5 +46,13 @@ export function useAuth() {
|
||||
return (await api.get('/auth/me', { silent: true })) as unknown as AuthUser
|
||||
}
|
||||
|
||||
return { signup, resendVerification, login, logout, fetchMe }
|
||||
return {
|
||||
signup,
|
||||
resendVerification,
|
||||
forgotPassword,
|
||||
resetPassword,
|
||||
login,
|
||||
logout,
|
||||
fetchMe,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user