refactor: use query parameter only when looking for password reset token

This commit is contained in:
kolaente 2025-06-03 18:30:47 +02:00
parent 59aca9fd5d
commit 3658cde42f
6 changed files with 10 additions and 49 deletions

View File

@ -15,7 +15,7 @@ context('Password Reset', () => {
const token: TokenAttributes = tokenArray[0] as TokenAttributes const token: TokenAttributes = tokenArray[0] as TokenAttributes
cy.visit(`/?userPasswordReset=${token.token}`) cy.visit(`/?userPasswordReset=${token.token}`)
cy.url().should('include', `/password-reset?token=${token.token}`) cy.url().should('include', `/password-reset?userPasswordReset=${token.token}`)
const newPassword = 'newSecurePassword123' const newPassword = 'newSecurePassword123'
cy.get('input[id=password]').type(newPassword) cy.get('input[id=password]').type(newPassword)
@ -34,7 +34,7 @@ context('Password Reset', () => {
it('Should show an error for an invalid token', () => { it('Should show an error for an invalid token', () => {
cy.visit('/?userPasswordReset=invalidtoken123') cy.visit('/?userPasswordReset=invalidtoken123')
cy.url().should('include', '/password-reset?token=invalidtoken123') cy.url().should('include', '/password-reset?userPasswordReset=invalidtoken123')
// Attempt to reset password // Attempt to reset password
const newPassword = 'newSecurePassword123' const newPassword = 'newSecurePassword123'

View File

@ -77,17 +77,6 @@ watch(accountDeletionConfirm, async (accountDeletionConfirm) => {
authStore.refreshUserInfo() authStore.refreshUserInfo()
}, { immediate: true }) }, { immediate: true })
// setup password reset redirect
const userPasswordReset = computed(() => route.query?.userPasswordReset as (string | undefined))
watch(userPasswordReset, (userPasswordReset) => {
if (userPasswordReset === undefined) {
return
}
authStore.setPasswordResetToken(userPasswordReset)
router.push({name: 'user.password-reset.reset', query: { token: userPasswordReset }})
}, { immediate: true })
// setup email verification redirect // setup email verification redirect
const userEmailConfirm = computed(() => route.query?.userEmailConfirm as (string | undefined)) const userEmailConfirm = computed(() => route.query?.userEmailConfirm as (string | undefined))
watch(userEmailConfirm, (userEmailConfirm) => { watch(userEmailConfirm, (userEmailConfirm) => {

View File

@ -8,7 +8,6 @@ import {getNextWeekDate} from '@/helpers/time/getNextWeekDate'
import {LINK_SHARE_HASH_PREFIX} from '@/constants/linkShareHash' import {LINK_SHARE_HASH_PREFIX} from '@/constants/linkShareHash'
import {useAuthStore} from '@/stores/auth' import {useAuthStore} from '@/stores/auth'
import {useBaseStore} from '@/stores/base'
import Login from '@/views/user/Login.vue' import Login from '@/views/user/Login.vue'
import Register from '@/views/user/Register.vue' import Register from '@/views/user/Register.vue'
@ -396,13 +395,14 @@ export async function getAuthForRoute(to: RouteLocation, authStore) {
// Check if password reset token is in query params // Check if password reset token is in query params
const resetToken = to.query.userPasswordReset as string | undefined const resetToken = to.query.userPasswordReset as string | undefined
if (resetToken) {
authStore.setPasswordResetToken(resetToken)
}
// Redirect to password reset page if we have a token stored // Redirect to password reset page if we have a token stored
if (authStore.passwordResetToken && to.name !== 'user.password-reset.reset') { if (resetToken && to.name !== 'user.password-reset.reset') {
return {name: 'user.password-reset.reset', query: { token: authStore.passwordResetToken }} return {name: 'user.password-reset.reset', query: { userPasswordReset: resetToken }}
}
if (typeof resetToken === 'undefined' && to.name === 'user.password-reset.reset') {
return {name: 'user.login'}
} }
// Check if the route the user wants to go to is a route which needs authentication. We use this to // Check if the route the user wants to go to is a route which needs authentication. We use this to
@ -415,21 +415,12 @@ export async function getAuthForRoute(to: RouteLocation, authStore) {
'link-share.auth', 'link-share.auth',
'openid.auth', 'openid.auth',
].includes(to.name as string) && ].includes(to.name as string) &&
authStore.passwordResetToken === null && localStorage.getItem('emailConfirmToken') === null
localStorage.getItem('emailConfirmToken') === null &&
!(to.name === 'home' && (typeof to.query.userPasswordReset !== 'undefined' || typeof to.query.userEmailConfirm !== 'undefined'))
if (isValidUserAppRoute) { if (isValidUserAppRoute) {
saveLastVisited(to.name as string, to.params, to.query) saveLastVisited(to.name as string, to.params, to.query)
} }
const baseStore = useBaseStore()
// When trying this before the current user was fully loaded we might get a flash of the login screen
// in the user shell. To make sure this does not happen we check if everything is ready before trying.
if (!baseStore.ready) {
return
}
if (isValidUserAppRoute) { if (isValidUserAppRoute) {
return {name: 'user.login'} return {name: 'user.login'}
} }

View File

@ -68,7 +68,6 @@ export const useAuthStore = defineStore('auth', () => {
const authenticated = ref(false) const authenticated = ref(false)
const isLinkShareAuth = ref(false) const isLinkShareAuth = ref(false)
const needsTotpPasscode = ref(false) const needsTotpPasscode = ref(false)
const passwordResetToken = ref<string | null>(null)
const info = ref<IUser | null>(null) const info = ref<IUser | null>(null)
const avatarUrl = ref('') const avatarUrl = ref('')
@ -150,10 +149,6 @@ export const useAuthStore = defineStore('auth', () => {
needsTotpPasscode.value = newNeedsTotpPasscode needsTotpPasscode.value = newNeedsTotpPasscode
} }
function setPasswordResetToken(token: string | null) {
passwordResetToken.value = token
}
function reloadAvatar() { function reloadAvatar() {
if (!info.value) return if (!info.value) return
avatarUrl.value = `${getAvatarUrl(info.value)}&=${new Date().valueOf()}` avatarUrl.value = `${getAvatarUrl(info.value)}&=${new Date().valueOf()}`
@ -449,7 +444,6 @@ export const useAuthStore = defineStore('auth', () => {
authenticated: readonly(authenticated), authenticated: readonly(authenticated),
isLinkShareAuth: readonly(isLinkShareAuth), isLinkShareAuth: readonly(isLinkShareAuth),
needsTotpPasscode: readonly(needsTotpPasscode), needsTotpPasscode: readonly(needsTotpPasscode),
passwordResetToken: readonly(passwordResetToken),
info: readonly(info), info: readonly(info),
avatarUrl: readonly(avatarUrl), avatarUrl: readonly(avatarUrl),
@ -472,7 +466,6 @@ export const useAuthStore = defineStore('auth', () => {
setAuthenticated, setAuthenticated,
setIsLinkShareAuth, setIsLinkShareAuth,
setNeedsTotpPasscode, setNeedsTotpPasscode,
setPasswordResetToken,
reloadAvatar, reloadAvatar,
updateLastUserRefresh, updateLastUserRefresh,

View File

@ -1,9 +1,7 @@
import {ref, computed, readonly} from 'vue' import {ref, computed, readonly} from 'vue'
import {useI18n} from 'vue-i18n' import {useI18n} from 'vue-i18n'
import {useRouter, useRoute} from 'vue-router'
import {defineStore, acceptHMRUpdate} from 'pinia' import {defineStore, acceptHMRUpdate} from 'pinia'
import {getAuthForRoute} from '@/router'
import {getBlobFromBlurHash} from '@/helpers/getBlobFromBlurHash' import {getBlobFromBlurHash} from '@/helpers/getBlobFromBlurHash'
import ProjectModel from '@/models/project' import ProjectModel from '@/models/project'
@ -22,9 +20,6 @@ export const useBaseStore = defineStore('base', () => {
const {t} = useI18n() const {t} = useI18n()
const router = useRouter()
const route = useRoute()
const ready = ref(false) const ready = ref(false)
const error = ref('') const error = ref('')
const loading = computed(() => !ready.value && error.value === '') const loading = computed(() => !ready.value && error.value === '')
@ -147,10 +142,6 @@ export const useBaseStore = defineStore('base', () => {
await checkAndSetApiUrl(window.API_URL) await checkAndSetApiUrl(window.API_URL)
await authStore.checkAuth() await authStore.checkAuth()
ready.value = true ready.value = true
const redirectTo = await getAuthForRoute(route, authStore)
if (typeof redirectTo !== 'undefined') {
await router.push(redirectTo)
}
} catch (e: unknown) { } catch (e: unknown) {
if (e instanceof NoApiUrlProvidedError) { if (e instanceof NoApiUrlProvidedError) {
error.value = ERROR_NO_API_URL error.value = ERROR_NO_API_URL

View File

@ -54,7 +54,6 @@
import {ref, reactive} from 'vue' import {ref, reactive} from 'vue'
import {useRoute} from 'vue-router' import {useRoute} from 'vue-router'
import {useI18n} from 'vue-i18n' import {useI18n} from 'vue-i18n'
import {useAuthStore} from '@/stores/auth'
import PasswordResetModel from '@/models/passwordReset' import PasswordResetModel from '@/models/passwordReset'
import PasswordResetService from '@/services/passwordReset' import PasswordResetService from '@/services/passwordReset'
@ -66,7 +65,6 @@ const credentials = reactive({
}) })
const route = useRoute() const route = useRoute()
const authStore = useAuthStore()
const {t} = useI18n() const {t} = useI18n()
const passwordResetService = reactive(new PasswordResetService()) const passwordResetService = reactive(new PasswordResetService())
@ -75,7 +73,7 @@ const successMessage = ref('')
async function resetPassword() { async function resetPassword() {
errorMsg.value = '' errorMsg.value = ''
const token = route.query.token as string const token = route.query.userPasswordReset as string
if (!token) { if (!token) {
errorMsg.value = t('user.auth.passwordResetTokenMissing') errorMsg.value = t('user.auth.passwordResetTokenMissing')
@ -90,7 +88,6 @@ async function resetPassword() {
try { try {
const {message} = await passwordResetService.resetPassword(passwordReset) const {message} = await passwordResetService.resetPassword(passwordReset)
successMessage.value = message successMessage.value = message
authStore.setPasswordResetToken(null)
} catch (e) { } catch (e) {
errorMsg.value = e.response.data.message errorMsg.value = e.response.data.message
} }