Files
crewli/apps/app/src/pages/forgot-password.vue
bert.hausmans 824b28897e fix: don't show success on validation error in forgot-password forms
The catch-all error handler (for anti-email-enumeration) was also
swallowing 422 validation errors, making it appear that a reset
email was sent even for empty or invalid input. Now 422 responses
are excluded from the catch — the user stays on the form so the
field-level validation messages remain visible.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 20:53:03 +02:00

181 lines
4.8 KiB
Vue

<script setup lang="ts">
import { useGenerateImageVariant } from '@core/composable/useGenerateImageVariant'
import authV2ForgotPasswordIllustrationDark from '@images/pages/auth-v2-forgot-password-illustration-dark.png'
import authV2ForgotPasswordIllustrationLight from '@images/pages/auth-v2-forgot-password-illustration-light.png'
import authV2MaskDark from '@images/pages/misc-mask-dark.png'
import authV2MaskLight from '@images/pages/misc-mask-light.png'
import { VNodeRenderer } from '@layouts/components/VNodeRenderer'
import { themeConfig } from '@themeConfig'
import { requiredValidator, emailValidator } from '@core/utils/validators'
import { VForm } from 'vuetify/components/VForm'
import { apiClient } from '@/lib/axios'
definePage({
meta: {
layout: 'blank',
public: true,
},
})
const refVForm = ref<VForm>()
const email = ref('')
const isSubmitting = ref(false)
const done = ref(false)
const authThemeImg = useGenerateImageVariant(
authV2ForgotPasswordIllustrationLight,
authV2ForgotPasswordIllustrationDark,
)
const authThemeMask = useGenerateImageVariant(authV2MaskLight, authV2MaskDark)
async function onSubmit(): Promise<void> {
const { valid } = await refVForm.value!.validate()
if (!valid) return
isSubmitting.value = true
try {
await apiClient.post('/auth/forgot-password', {
email: email.value.trim(),
app: 'app',
})
done.value = true
}
catch (error: unknown) {
const ax = error as { response?: { status?: number } }
if (ax.response?.status === 422) {
// Validation error — don't show success, let the user fix input
return
}
// For all other errors (404 user-not-found, network, etc.):
// show generic success to prevent email enumeration
done.value = true
}
finally {
isSubmitting.value = false
}
}
</script>
<template>
<RouterLink to="/">
<div class="auth-logo d-flex align-center gap-x-3">
<VNodeRenderer :nodes="themeConfig.app.logo" />
<h1 class="auth-title">
{{ themeConfig.app.title }}
</h1>
</div>
</RouterLink>
<VRow
class="auth-wrapper bg-surface"
no-gutters
>
<VCol
md="8"
class="d-none d-md-flex"
>
<div class="position-relative bg-background w-100 me-0">
<div
class="d-flex align-center justify-center w-100 h-100"
style="padding-inline: 150px;"
>
<VImg
max-width="468"
:src="authThemeImg"
class="auth-illustration mt-16 mb-2"
/>
</div>
<img
class="auth-footer-mask flip-in-rtl"
:src="authThemeMask"
alt="auth-footer-mask"
height="280"
width="100"
>
</div>
</VCol>
<VCol
cols="12"
md="4"
class="auth-card-v2 d-flex align-center justify-center"
>
<VCard
flat
:max-width="500"
class="mt-12 mt-sm-0 pa-6"
>
<VCardText>
<h4 class="text-h4 mb-1">
Wachtwoord vergeten?
</h4>
<p class="mb-0">
Vul je e-mailadres in en we sturen je een link om je wachtwoord te herstellen.
</p>
</VCardText>
<VCardText>
<VAlert
v-if="done"
type="success"
variant="tonal"
class="mb-4"
>
Als dit e-mailadres bij ons bekend is, ontvang je een link om je wachtwoord te herstellen.
</VAlert>
<VForm
v-if="!done"
ref="refVForm"
@submit.prevent="onSubmit"
>
<VRow>
<VCol cols="12">
<AppTextField
v-model="email"
autofocus
label="E-mailadres"
type="email"
placeholder="naam@voorbeeld.nl"
:rules="[requiredValidator, emailValidator]"
/>
</VCol>
<VCol cols="12">
<VBtn
block
type="submit"
:loading="isSubmitting"
>
Verstuur herstelmail
</VBtn>
</VCol>
<VCol cols="12">
<RouterLink
class="d-flex align-center justify-center"
:to="{ name: 'login' }"
>
<VIcon
icon="tabler-chevron-left"
size="20"
class="me-1 flip-in-rtl"
/>
<span>Terug naar inloggen</span>
</RouterLink>
</VCol>
</VRow>
</VForm>
</VCardText>
</VCard>
</VCol>
</VRow>
</template>
<style lang="scss">
@use "@core/scss/template/pages/page-auth";
</style>