skip to Main Content

I have a problem with redirecting after submitting form in SvelteKit.

I have some routes that needs authentication, before rendering it I’m checking if user is authenticated.

routes
│       ├── +page.svelte
│       ├── login
│       │   ├── +page.server.ts
│       │   └── +page.svelte
│       └── movies
│           ├── +page.server.ts
│           ├── +page.svelte
│           └── [id]
│               ├── +page.server.ts
│               ├── +page.svelte
│               └── +page.ts

For example, when user vistits /movies route, in my +page.server.ts I’m checking if user is authenticated, by lookup in cookies. If cookies haven’t auth_token I’m redirecting him to /login route with some info about desired redirecting route after succesful login.

So in that scenario, user will be redirected to /login?redirectTo=%2Fmovies

For that part, everything works great.

The idea is that, if he authenticate it should redirect him again to /movies route.

I wrote some logic in routes/login/+page.server.ts to gather value of redirectTo query param but after submitting form I can’t see it.

/routes/login/+page.server.ts

import { redirect } from '@sveltejs/kit';

    export const actions = {
        login: async ({request, cookies, url}) => {
            
            console.log("url", url)
            console.log('request', request)
            const formData = await request.formData();
            const data = { email: formData.get('email'), password: formData.get('password')}

            

                const response = await fetch("http://localhost:4000/v1/tokens/authentication", {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify(data),
                    credentials: 'include'
                })

                if (!response.ok) {
                    return {success: false};
                }
                
                const cookie = response.headers.get('set-cookie');
                if (cookie !== null) {
                const parts = cookie.split(';');
                const token = parts[0].split('=')[1];
                const expires = parts.find(p => p.trim().startsWith('Expires'))?.split('=')[1];

                cookies.set('auth_token', token, {
                    path: '/',
                    httpOnly: true,
                    sameSite: 'lax',
                    expires: expires ? new Date(expires) : undefined
                });

            
                console.log(url.searchParams)

                
                const redirectTo = url.searchParams.get('redirectTo') || '/'
                console.log("redirectTo", redirectTo)
                redirect(303, redirectTo);

                return {
                    
                    success: true,
                }
                
            
            } return {
                success: false,
            }
            
                
            
        }
}

/routes/login/+page.svelte

<script lang="ts">
  import { enhance } from "$app/forms";
</script>

<h1>Login</h1>
<form method="POST" action="?/login" use:enhance>
  <label
    >Email
    <input name="email" type="email" required />
  </label>
  <label
    >password
    <input name="password" type="password" required />
  </label>
  <button>Log in</button>
</form>

The output of console.log("url", url) is missing that URL with redirectTo value.

url URL {
  href: 'http://localhost:5173/login?/login',
  origin: 'http://localhost:5173',
  protocol: 'http:',
  username: '',
  password: '',
  host: 'localhost:5173',
  hostname: 'localhost',
  port: '5173',
  pathname: '/login',
  search: '?/login',
  searchParams: URLSearchParams { '/login' => '' },
  hash: ''
}

What Am I doing wrong? I followed logic from docs.

Solution

I managed to solve this problem.
I’ve used use:enhanced on Login form, to pass search param redirectTo from URL into action.

+page.svelte for login route

<script lang="ts">
  import { enhance } from "$app/forms";
  import { page } from "$app/stores";
  let currentUrl = $page.url.href;
  const url: URL = new URL(currentUrl);
  const redirectTo: string = url.searchParams.get("redirectTo") ?? "";
</script>

<h1>Login</h1>
<h1>{currentUrl}</h1>
<h2>{redirectTo}</h2>
<form
  method="POST"
  action="?/login"
  use:enhance={({ formData }) => {
    formData.append("redirectTo", redirectTo);
  }}
>
  <label
    >Email
    <input name="email" type="email" required />
  </label>
  <label
    >password
    <input name="password" type="password" required />
  </label>
  <button>Log in</button>
</form>

+page.server.ts for login route actions

    import { redirect } from '@sveltejs/kit';

    export const actions = {
        login: async ({request, cookies, url}) => {
            
           
            const formData = await request.formData();
            const data = { email: formData.get('email'), password: formData.get('password'), redirectTo: formData.get("redirectTo")}
            console.log("data", data)
            

            

                const response = await fetch("http://localhost:4000/v1/tokens/authentication", {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({email: data.email, password: data.password}),
                    credentials: 'include'
                })

                if (!response.ok) {
                    console.log('err', response)
                    return {success: false};
                }
                
                const cookie = response.headers.get('set-cookie');
                if (cookie !== null) {
                const parts = cookie.split(';');
                const token = parts[0].split('=')[1];
                const expires = parts.find(p => p.trim().startsWith('Expires'))?.split('=')[1];

                cookies.set('auth_token', token, {
                    path: '/',
                    httpOnly: true,
                    sameSite: 'lax',
                    expires: expires ? new Date(expires) : undefined
                });

            
                console.log(url.searchParams)

                
                const redirectTo = data.redirectTo != null ? data.redirectTo.toString() : "/"
                console.log("redirectTo", redirectTo)
                
                redirect(303, redirectTo);   
            
            } return {
                success: false,
            }
            
                
            
        },
        register: async (event) => {
            console.log('elo')
        }
    }

2

Answers


  1. On the /routes/login/+page.svelte page, if a query string parameter named redirectTo exists, then you can access it with:

    import { page } from '$app/stores'
    
    $page.url.searchParams.get('redirectTo')
    

    In the same file, you need to check if that query string parameter has a value, and then add it as a query string parameter to the action attribute of the <form>. So then when the <form> is submitted and the server receives that POST request, you can read out the value of that query string parameter using url.searchParams.get('redirectTo') in the /routes/login/+page.server.ts file.

    If you want to make things easier, use the default action on the server, and then you can use action="" or skip it entirely (both means the POST request will be sent to the same URL the current page is one, including the query string if one is present).

    Login or Signup to reply.
  2. You can solve this more cleanly by appending the URL search parameters (which includes your redirectTo parameter) in the form action URL.

    <!-- If the URL has a query string, append it -->
    <form
      method="POST"
      action="?/login{$page.url.search ? `&${$page.url.search}` : ''}"
      use:enhance
    >
    

    Or, if you want it in formdata instead of the URL:

        <input type="hidden" name="redirectTo"
          value={$page.url.searchParams.get("redirectTo") ?? ""} />
    

    Regardless of how you solve it, please ensure that you validate the redirectTo parameter on the backend to prevent redirecting to phishing sites.

    https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html#unvalidated-redirects-and-forwards-cheat-sheet

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search