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.
This commit is contained in:
parent
25f9f5dceb
commit
f68bb2625e
@ -5,16 +5,24 @@ export function useRedirectToLastVisited() {
|
|||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
function redirectIfSaved() {
|
function getRedirectRoute() {
|
||||||
const last = getLastVisited()
|
const last = getLastVisited()
|
||||||
if (last !== null) {
|
if (last !== null) {
|
||||||
router.push({
|
clearLastVisited()
|
||||||
|
return {
|
||||||
name: last.name,
|
name: last.name,
|
||||||
params: last.params,
|
params: last.params,
|
||||||
query: last.query,
|
query: last.query,
|
||||||
})
|
}
|
||||||
clearLastVisited()
|
}
|
||||||
return
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
function redirectIfSaved() {
|
||||||
|
const lastRoute = getRedirectRoute()
|
||||||
|
if (lastRoute) {
|
||||||
|
router.push(lastRoute)
|
||||||
}
|
}
|
||||||
|
|
||||||
router.push({name: 'home'})
|
router.push({name: 'home'})
|
||||||
@ -22,5 +30,6 @@ export function useRedirectToLastVisited() {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
redirectIfSaved,
|
redirectIfSaved,
|
||||||
|
getRedirectRoute,
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,6 +6,7 @@ import {saveProjectView, getProjectView} from '@/helpers/projectView'
|
|||||||
import {parseDateOrString} from '@/helpers/time/parseDateOrString'
|
import {parseDateOrString} from '@/helpers/time/parseDateOrString'
|
||||||
import {getNextWeekDate} from '@/helpers/time/getNextWeekDate'
|
import {getNextWeekDate} from '@/helpers/time/getNextWeekDate'
|
||||||
import {setTitle} from '@/helpers/setTitle'
|
import {setTitle} from '@/helpers/setTitle'
|
||||||
|
import {getToken} from '@/helpers/auth'
|
||||||
|
|
||||||
import {useProjectStore} from '@/stores/projects'
|
import {useProjectStore} from '@/stores/projects'
|
||||||
import {useAuthStore} from '@/stores/auth'
|
import {useAuthStore} from '@/stores/auth'
|
||||||
@ -71,6 +72,8 @@ const NewProjectComponent = () => import('@/views/project/NewProject.vue')
|
|||||||
const EditTeamComponent = () => import('@/views/teams/EditTeam.vue')
|
const EditTeamComponent = () => import('@/views/teams/EditTeam.vue')
|
||||||
const NewTeamComponent = () => import('@/views/teams/NewTeam.vue')
|
const NewTeamComponent = () => import('@/views/teams/NewTeam.vue')
|
||||||
|
|
||||||
|
const linkShareHashPrefix = '#linkshare='
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(import.meta.env.BASE_URL),
|
history: createWebHistory(import.meta.env.BASE_URL),
|
||||||
scrollBehavior(to, from, savedPosition) {
|
scrollBehavior(to, from, savedPosition) {
|
||||||
@ -80,7 +83,7 @@ const router = createRouter({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Scroll to anchor should still work
|
// Scroll to anchor should still work
|
||||||
if (to.hash) {
|
if (to.hash && !to.hash.startsWith(linkShareHashPrefix)) {
|
||||||
return {el: to.hash}
|
return {el: to.hash}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -482,8 +485,36 @@ export async function getAuthForRoute(route: RouteLocation) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
router.beforeEach(async (to) => {
|
router.beforeEach(async (to, from) => {
|
||||||
return getAuthForRoute(to)
|
|
||||||
|
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
|
export default router
|
@ -43,9 +43,11 @@ import {PROJECT_VIEWS, type ProjectView} from '@/types/ProjectView'
|
|||||||
|
|
||||||
import {useBaseStore} from '@/stores/base'
|
import {useBaseStore} from '@/stores/base'
|
||||||
import {useAuthStore} from '@/stores/auth'
|
import {useAuthStore} from '@/stores/auth'
|
||||||
|
import {useRedirectToLastVisited} from '@/composables/useRedirectToLastVisited'
|
||||||
|
|
||||||
const {t} = useI18n({useScope: 'global'})
|
const {t} = useI18n({useScope: 'global'})
|
||||||
useTitle(t('sharing.authenticating'))
|
useTitle(t('sharing.authenticating'))
|
||||||
|
const {getRedirectRoute} = useRedirectToLastVisited()
|
||||||
|
|
||||||
function useAuth() {
|
function useAuth() {
|
||||||
const baseStore = useBaseStore()
|
const baseStore = useBaseStore()
|
||||||
@ -59,6 +61,7 @@ function useAuth() {
|
|||||||
const password = ref('')
|
const password = ref('')
|
||||||
|
|
||||||
const authLinkShare = computed(() => authStore.authLinkShare)
|
const authLinkShare = computed(() => authStore.authLinkShare)
|
||||||
|
const linkShareHashPrefix = '#linkshare='
|
||||||
|
|
||||||
async function authenticate() {
|
async function authenticate() {
|
||||||
authenticateWithPassword.value = false
|
authenticateWithPassword.value = false
|
||||||
@ -87,7 +90,22 @@ function useAuth() {
|
|||||||
? route.query.view
|
? route.query.view
|
||||||
: 'list'
|
: '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) {
|
} catch (e: any) {
|
||||||
if (e.response?.data?.code === 13001) {
|
if (e.response?.data?.code === 13001) {
|
||||||
authenticateWithPassword.value = true
|
authenticateWithPassword.value = true
|
||||||
|
Loading…
x
Reference in New Issue
Block a user