fix: secure email change recovery flow
This commit is contained in:
@@ -25,7 +25,11 @@ onMounted(async () => {
|
||||
try {
|
||||
const resp = await verifyEmail(token.value)
|
||||
message.value = resp.message
|
||||
auth.markEmailVerified()
|
||||
if (resp.session_invalidated) {
|
||||
auth.logout()
|
||||
} else {
|
||||
auth.markEmailVerified()
|
||||
}
|
||||
} catch (err) {
|
||||
if (err instanceof ApiError) {
|
||||
error.value = `[${err.code}] ${err.message}`
|
||||
|
||||
@@ -9,7 +9,7 @@ const auth = useAuthStore()
|
||||
|
||||
const loading = ref(true)
|
||||
|
||||
const profileForm = ref({ email: '', username: '' })
|
||||
const profileForm = ref({ email: '', username: '', currentPassword: '' })
|
||||
const profileBusy = ref(false)
|
||||
const profileMessage = ref<string | null>(null)
|
||||
const profileError = ref<string | null>(null)
|
||||
@@ -24,9 +24,13 @@ const verificationMessage = ref<string | null>(null)
|
||||
const verificationError = ref<string | null>(null)
|
||||
|
||||
const canResendVerification = computed(() => Boolean(auth.user && !auth.user.email_verified))
|
||||
const emailChangeRequested = computed(() => {
|
||||
const email = profileForm.value.email.trim().toLowerCase()
|
||||
return Boolean(email && auth.user && email !== auth.user.email)
|
||||
})
|
||||
|
||||
function syncProfile(user: UserProfile) {
|
||||
profileForm.value = { email: user.email ?? '', username: user.username ?? '' }
|
||||
profileForm.value = { email: user.email ?? '', username: user.username ?? '', currentPassword: '' }
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
@@ -77,9 +81,16 @@ async function saveProfile() {
|
||||
try {
|
||||
const email = profileForm.value.email.trim().toLowerCase()
|
||||
const username = profileForm.value.username.trim()
|
||||
const payload: { email?: string; username?: string } = {}
|
||||
const payload: { email?: string; username?: string; current_password?: string } = {}
|
||||
|
||||
if (email && email !== auth.user.email) payload.email = email
|
||||
if (email && email !== auth.user.email) {
|
||||
if (!profileForm.value.currentPassword) {
|
||||
profileError.value = '修改邮箱需要输入当前密码'
|
||||
return
|
||||
}
|
||||
payload.email = email
|
||||
payload.current_password = profileForm.value.currentPassword
|
||||
}
|
||||
if (username && username !== auth.user.username) payload.username = username
|
||||
|
||||
if (!payload.email && !payload.username) {
|
||||
@@ -88,7 +99,11 @@ async function saveProfile() {
|
||||
}
|
||||
|
||||
const resp = await updateProfile(auth.token, payload)
|
||||
auth.updateUser(resp.user)
|
||||
if (resp.token) {
|
||||
auth.setAuth(resp.token, resp.user)
|
||||
} else {
|
||||
auth.updateUser(resp.user)
|
||||
}
|
||||
syncProfile(resp.user)
|
||||
profileMessage.value = resp.message
|
||||
} catch (err) {
|
||||
@@ -178,6 +193,24 @@ async function changePassword() {
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<label v-if="emailChangeRequested" class="mt-3 block max-w-md space-y-1">
|
||||
<div class="text-xs font-medium text-slate-600">当前密码(修改邮箱必填)</div>
|
||||
<input
|
||||
v-model="profileForm.currentPassword"
|
||||
type="password"
|
||||
autocomplete="current-password"
|
||||
class="w-full rounded-md border border-slate-200 bg-white px-3 py-2 text-sm text-slate-800"
|
||||
placeholder="用于确认是本人操作"
|
||||
/>
|
||||
</label>
|
||||
|
||||
<div
|
||||
v-if="auth.user?.pending_email"
|
||||
class="mt-3 rounded-lg border border-amber-200 bg-amber-50 p-3 text-sm text-amber-900"
|
||||
>
|
||||
待确认新邮箱:{{ auth.user.pending_email }}。确认前仍使用当前邮箱登录和找回密码。
|
||||
</div>
|
||||
|
||||
<div v-if="profileMessage" class="mt-4 rounded-lg border border-emerald-200 bg-emerald-50 p-3 text-sm text-emerald-900">
|
||||
{{ profileMessage }}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user