DEV Community

myleftshoe
myleftshoe

Posted on

Prevent back navigation after login.

sveltekit hooks can be used to protect all routes in your app by redirecting to a login page as in this simple example:

hooks.server.ts

import { redirect, type Handle } from '@sveltejs/kit'
import { AUTH_COOKIE } from '$env/static/private'

export const handle: Handle = async ({ event, resolve }) => {
    if (event.url.pathname.startsWith('/login')) {
        return await resolve(event)
    }

    if (event.cookies.get('AUTH_COOKIE') !== AUTH_COOKIE) {
        redirect(303, '/login')
    }
    return await resolve(event)
}
Enter fullscreen mode Exit fullscreen mode

This works well, but but the redirect makes the login page part of the browser's history, meaning the user can navigate back to it. Maybe you don't want that - it's a pretty ugly experience especially swiping back to it on mobile.

I spent way too long trying to solve this with ChatGPT sending me on a wild goose chase. In the end the solution was simple: use svelte's goto with replaceState after successful login:

login.svelte

<script lang="ts">
    import { enhance } from '$app/forms'
    import { goto } from '$app/navigation'
    import type { PageProps } from './$types'

    let { form }: PageProps = $props()

    $effect(() => {
        if (form?.success) {
            goto('/', { replaceState: true })
        }
    })
</script>

<main class="h-full">
    <div class="flex h-full flex-col items-center justify-center gap-4">
        <form class="grid place-content-center gap-4" method="POST" action="?/login" use:enhance>
            <input class="input" name="username" type="text" placeholder="Username" autofocus required />
            <input class="input" name="password" type="password" placeholder="Password" required />
            <button class="btn preset-filled-primary-500">Log in</button>
        </form>
        <p class="text-error-500 {form?.error ? 'opacity-100' : 'opacity-0'}">{form?.error ?? '*'}</p>
    </div>
</main>
Enter fullscreen mode Exit fullscreen mode

The login form is backed by a form action in +page.server.ts. The important thing here is to not redirect after login, that is done using the goto in login.svelte

export const actions = {
    login: async ({ cookies, request }) => {
        const data = await request.formData()

        const username = data.get('username') as string
        const password = data.get('password') as string

        const hashedPassword = md5Hash(password)

        const token = await login(username, hashedPassword)

        if (!token) {
            return { error: 'Invalid username or password' }
        }

        cookies.set('AUTH_COOKIE', AUTH_COOKIE, {
            path: '/',
            httpOnly: true,
            secure: true,
            sameSite: 'strict',
            maxAge: 60 * 60 * 24 * 365 * 10, // 10 years
        })

        return { success: true }
        // redirect(303, '/')
    },
} satisfies Actions
Enter fullscreen mode Exit fullscreen mode

Once you go goto, you can't go back!

Heroku

Build apps, not infrastructure.

Dealing with servers, hardware, and infrastructure can take up your valuable time. Discover the benefits of Heroku, the PaaS of choice for developers since 2007.

Visit Site

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay