feat(auth): add password reset flow with OTP

- Add password-reset Prisma schema for OTP tracking
- Add API contract schemas (start, verify, resend)
- Add password-reset service with OTP validation and email sending
- Add frontend reset-password page with 2-step flow
- Add InputOTP component usage for OTP input
- Add link to reset-password in login page
- Remove link to About in login
- Center and resize login/reset password forms
This commit is contained in:
Jose Selesan
2026-04-13 09:10:06 -03:00
parent 566291fb43
commit 70273f6760
13 changed files with 906 additions and 7 deletions

View File

@@ -94,5 +94,19 @@ export type {
} from './public-booking'
export { sportSchema, createSportSchema, updateSportSchema } from './sport'
export type { Sport, CreateSportInput, UpdateSportInput } from './sport'
export { userProfileSchema } from './user'
export type { UserProfile, UserProfileResponse } from './user'
export {
userProfileSchema,
passwordResetStartSchema,
passwordResetVerifySchema,
passwordResetResendSchema,
} from './user'
export type {
UserProfile,
UserProfileResponse,
PasswordResetStartInput,
PasswordResetVerifyInput,
PasswordResetResendInput,
PasswordResetStartResponse,
PasswordResetVerifyResponse,
PasswordResetResendResponse,
} from './user'

View File

@@ -11,3 +11,43 @@ export const userProfileSchema = z.object({
export type UserProfile = z.infer<typeof userProfileSchema>
export type UserProfileResponse = UserProfile
export const passwordResetStartSchema = z.object({
email: z.string().email('Ingresá un email válido.'),
})
export type PasswordResetStartInput = z.infer<typeof passwordResetStartSchema>
export const passwordResetVerifySchema = z.object({
requestId: z.string().uuid('Request ID inválido.'),
otp: z.string().length(6, 'El código debe tener 6 dígitos.'),
newPassword: z.string().min(6, 'La contraseña debe tener al menos 6 caracteres.'),
})
export type PasswordResetVerifyInput = z.infer<typeof passwordResetVerifySchema>
export const passwordResetResendSchema = z.object({
requestId: z.string().uuid('Request ID inválido.'),
})
export type PasswordResetResendInput = z.infer<typeof passwordResetResendSchema>
export type PasswordResetStartResponse = {
message: string;
requestId: string;
email: string;
expiresAt: string;
cooldownSeconds: number;
}
export type PasswordResetVerifyResponse = {
message: string;
}
export type PasswordResetResendResponse = {
message: string;
requestId: string;
email: string;
expiresAt: string;
cooldownSeconds: number;
}