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:
kolaente 2023-04-01 12:14:52 +02:00
parent 25f9f5dceb
commit f68bb2625e
Signed by untrusted user: konrad
GPG Key ID: F40E70337AB24C9B
3 changed files with 67 additions and 9 deletions

View File

@ -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,
}
}

View File

@ -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

View File

@ -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