From f68bb2625e5f619f365fdd421aeda2b8af879aab Mon Sep 17 00:00:00 2001 From: kolaente Date: Sat, 1 Apr 2023 12:14:52 +0200 Subject: [PATCH] feat: persist link share auth rule in url hash This allows sharing links to a task directly. We're using hashes instead of query parameters because hash values are usually not logged in access logs. With this change, when a user uses a link share, the link share hash will be appended to all urls while browsing. When a link share hash is encountered in the current url and the user is not authenticated, they will be redirected to the link share auth page, get authenticated and then get redirected to whatever url they were previously on. --- src/composables/useRedirectToLastVisited.ts | 19 ++++++++--- src/router/index.ts | 37 +++++++++++++++++++-- src/views/sharing/LinkSharingAuth.vue | 20 ++++++++++- 3 files changed, 67 insertions(+), 9 deletions(-) diff --git a/src/composables/useRedirectToLastVisited.ts b/src/composables/useRedirectToLastVisited.ts index 838028cb6..ffd3711a9 100644 --- a/src/composables/useRedirectToLastVisited.ts +++ b/src/composables/useRedirectToLastVisited.ts @@ -5,16 +5,24 @@ export function useRedirectToLastVisited() { const router = useRouter() - function redirectIfSaved() { + function getRedirectRoute() { const last = getLastVisited() if (last !== null) { - router.push({ + clearLastVisited() + return { name: last.name, params: last.params, query: last.query, - }) - clearLastVisited() - return + } + } + + return null + } + + function redirectIfSaved() { + const lastRoute = getRedirectRoute() + if (lastRoute) { + router.push(lastRoute) } router.push({name: 'home'}) @@ -22,5 +30,6 @@ export function useRedirectToLastVisited() { return { redirectIfSaved, + getRedirectRoute, } } \ No newline at end of file diff --git a/src/router/index.ts b/src/router/index.ts index a685542ce..869a7fb60 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -6,6 +6,7 @@ import {saveProjectView, getProjectView} from '@/helpers/projectView' import {parseDateOrString} from '@/helpers/time/parseDateOrString' import {getNextWeekDate} from '@/helpers/time/getNextWeekDate' import {setTitle} from '@/helpers/setTitle' +import {getToken} from '@/helpers/auth' import {useProjectStore} from '@/stores/projects' import {useAuthStore} from '@/stores/auth' @@ -71,6 +72,8 @@ const NewProjectComponent = () => import('@/views/project/NewProject.vue') const EditTeamComponent = () => import('@/views/teams/EditTeam.vue') const NewTeamComponent = () => import('@/views/teams/NewTeam.vue') +const linkShareHashPrefix = '#linkshare=' + const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), scrollBehavior(to, from, savedPosition) { @@ -80,7 +83,7 @@ const router = createRouter({ } // Scroll to anchor should still work - if (to.hash) { + if (to.hash && !to.hash.startsWith(linkShareHashPrefix)) { return {el: to.hash} } @@ -482,8 +485,36 @@ export async function getAuthForRoute(route: RouteLocation) { } } -router.beforeEach(async (to) => { - return getAuthForRoute(to) +router.beforeEach(async (to, from) => { + + if(from.hash && from.hash.startsWith(linkShareHashPrefix)) { + to.hash = from.hash + } + + if (to.hash.startsWith(linkShareHashPrefix)) { + const currentAuthToken = getToken() + if (currentAuthToken === null) { + saveLastVisited(to.name as string, to.params, to.query) + return { + name: 'link-share.auth', + params: { + share: to.hash.replace(linkShareHashPrefix, ''), + }, + } + } + } + + const newRoute = await getAuthForRoute(to) + if(newRoute) { + return { + ...newRoute, + hash: to.hash, + } + } + + if(!to.fullPath.endsWith(to.hash)) { + return to.fullPath + to.hash + } }) export default router \ No newline at end of file diff --git a/src/views/sharing/LinkSharingAuth.vue b/src/views/sharing/LinkSharingAuth.vue index dab64eb77..302223dec 100644 --- a/src/views/sharing/LinkSharingAuth.vue +++ b/src/views/sharing/LinkSharingAuth.vue @@ -43,9 +43,11 @@ import {PROJECT_VIEWS, type ProjectView} from '@/types/ProjectView' import {useBaseStore} from '@/stores/base' import {useAuthStore} from '@/stores/auth' +import {useRedirectToLastVisited} from '@/composables/useRedirectToLastVisited' const {t} = useI18n({useScope: 'global'}) useTitle(t('sharing.authenticating')) +const {getRedirectRoute} = useRedirectToLastVisited() function useAuth() { const baseStore = useBaseStore() @@ -59,6 +61,7 @@ function useAuth() { const password = ref('') const authLinkShare = computed(() => authStore.authLinkShare) + const linkShareHashPrefix = '#linkshare=' async function authenticate() { authenticateWithPassword.value = false @@ -87,7 +90,22 @@ function useAuth() { ? route.query.view : 'list' - router.push({name: `project.${view}`, params: {projectId}}) + const hash = linkShareHashPrefix + route.params.share + + const last = getRedirectRoute() + if (last) { + router.push({ + ...last, + hash, + }) + return + } + + router.push({ + name: `project.${view}`, + params: {projectId}, + hash, + }) } catch (e: any) { if (e.response?.data?.code === 13001) { authenticateWithPassword.value = true