feat(auth): send the user back to ?redirect= after login

Both login pages always landed on '/'. A page that needs an identity
(chatelet's Book button) can now bounce an anonymous user through
/login?redirect=<path> and get them back. Only same-origin absolute
paths are honoured; anything else falls back to home.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Padreug 2026-09-15 23:10:26 +02:00
commit 718d6e76e1
3 changed files with 25 additions and 8 deletions

View file

@ -77,3 +77,16 @@ export const catchAllRoute: RouteRecordRaw = {
path: '/:pathMatch(.*)*', path: '/:pathMatch(.*)*',
redirect: '/', redirect: '/',
} }
/**
* Where to send the user after a successful login. Honours a `?redirect=`
* query param so a page can bounce an anonymous user through /login and
* back (e.g. chatelet's Book button). Only same-origin absolute paths are
* accepted — anything else (external URLs, protocol-relative `//host`)
* falls back to home.
*/
export function postLoginTarget(redirect: unknown): string {
if (typeof redirect !== 'string') return '/'
if (!redirect.startsWith('/') || redirect.startsWith('//')) return '/'
return redirect
}

View file

@ -111,7 +111,8 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed } from 'vue' import { ref, computed } from 'vue'
import { useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import { postLoginTarget } from '@/lib/router-helpers'
import { Card, CardContent } from '@/components/ui/card' import { Card, CardContent } from '@/components/ui/card'
import { Button } from '@/components/ui/button' import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input' import { Input } from '@/components/ui/input'
@ -120,6 +121,7 @@ import { auth } from '@/composables/useAuthService'
import { toast } from 'vue-sonner' import { toast } from 'vue-sonner'
const router = useRouter() const router = useRouter()
const route = useRoute()
const isLoading = ref(false) const isLoading = ref(false)
const error = ref('') const error = ref('')
const successMessage = ref('') const successMessage = ref('')
@ -155,9 +157,9 @@ async function handleLogin() {
successMessage.value = 'Login successful! Redirecting...' successMessage.value = 'Login successful! Redirecting...'
toast.success('Login successful!') toast.success('Login successful!')
// Redirect to home page after successful login // Back to where the user came from (?redirect=), else home
setTimeout(() => { setTimeout(() => {
router.push('/') router.push(postLoginTarget(route.query.redirect))
}, 1500) }, 1500)
} catch (err) { } catch (err) {

View file

@ -140,7 +140,8 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed } from 'vue' import { ref, computed } from 'vue'
import { useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import { postLoginTarget } from '@/lib/router-helpers'
import { Card, CardContent } from '@/components/ui/card' import { Card, CardContent } from '@/components/ui/card'
import { Button } from '@/components/ui/button' import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input' import { Input } from '@/components/ui/input'
@ -150,6 +151,7 @@ import { useDemoAccountGenerator } from '@/composables/useDemoAccountGenerator'
import { toast } from 'vue-sonner' import { toast } from 'vue-sonner'
const router = useRouter() const router = useRouter()
const route = useRoute()
const { isLoading, error, generateNewCredentials } = useDemoAccountGenerator() const { isLoading, error, generateNewCredentials } = useDemoAccountGenerator()
const successMessage = ref('') const successMessage = ref('')
const activeMode = ref<'demo' | 'login'>('demo') const activeMode = ref<'demo' | 'login'>('demo')
@ -186,9 +188,9 @@ async function createFakeAccount() {
successMessage.value = `Account created! Username: ${credentials.username}` successMessage.value = `Account created! Username: ${credentials.username}`
toast.success(`Logged in as ${credentials.username}!`) toast.success(`Logged in as ${credentials.username}!`)
// Redirect to home page after successful registration // Back to where the user came from (?redirect=), else home
setTimeout(() => { setTimeout(() => {
router.push('/') router.push(postLoginTarget(route.query.redirect))
}, 2000) }, 2000)
} catch (err) { } catch (err) {
@ -216,9 +218,9 @@ async function handleLogin() {
successMessage.value = 'Login successful! Redirecting...' successMessage.value = 'Login successful! Redirecting...'
toast.success('Login successful!') toast.success('Login successful!')
// Redirect to home page after successful login // Back to where the user came from (?redirect=), else home
setTimeout(() => { setTimeout(() => {
router.push('/') router.push(postLoginTarget(route.query.redirect))
}, 1500) }, 1500)
} catch (err) { } catch (err) {