authenticationsveltesveltekit

hooks.server and page.server authentication infinite loop


I am facing a problem with an infinite loop in my authentication process. I have a separate login system from my main page, which redirects to the /auth/callback path sending a token via URL parameters when the user logs in successfully. In this callback path, I have a +page.server.js file that receives the token and performs a validation via a request to an internal API. If the validation is successful, the session is stored in a cookie and the user is redirected to the home page (/).

On the other hand, in my hooks.server.js, the existence of this cookie is validated and, if it does not exist, the user is redirected to the login page. However, when I try to access my page, it redirects me to the login. When I successfully log in and return to the /auth/callback path, an infinite loop occurs, repeatedly redirecting me to login and back to callback without stopping. Interestingly, when I directly access my home page via the URL, the loop does not occur.

Based on the logs I've reviewed, it appears that the +page.server.js code is executing twice, while the hooks.server.js is executing once. Even though the cookie is copied correctly, the loop never stops, I don't know why this is happening or if I'm missing some settings.

Execution logs

cookie saved
redirecting to login
cookie saved
cookie saved
redirecting to login
cookie saved
cookie saved
redirecting to login

hooks.server.js

import { LOGIN_URL } from '$env/static/private';
import { redirect } from '@sveltejs/kit';
import cookie from 'cookie';

export const handle = async ({ event, resolve }) => {
    const requestedPath = event.url.pathname;
    const host = event.url.host;
    const publicPaths = ['/auth/callback', '/auth/logout'];

    if (publicPaths.includes(requestedPath)) {
        return resolve(event);
    }

    const cookies = cookie.parse(event.request.headers.get('cookie') || '');
    const email = cookies.email;

    if (!email) {
        console.log("redirecting to login");
        throw redirect(303, `${LOGIN_URL}?srv=${host}/auth/callback`);
    }

    return await resolve(event);
};

+page.server.js

import { validateToken } from '../../../lib/auth';
import { redirect } from '@sveltejs/kit';
import { LOGIN_URL } from '$env/static/private';

export const load = async ({ url, cookies }) => {

    const token = url.searchParams.get('apiToken');

    if (token) {

        const { success, data, error } = await validateToken(token);

        if (!success) {
            throw redirect(303, `${LOGIN_URL}/error?type=AuthError`);
        }

        const email = data.data.email;

        cookies.set('email', email, {
            httpOnly: true,
            secure: false,
            sameSite: 'strict',
            maxAge: 86400 * 1,
            path: '/'
        });

        console.log("cookie saved");

        throw redirect(303, '/');

    } else {
        throw redirect(303, `${LOGIN_URL}/error?type=AuthError`);
    }
};

I hope that when I receive my token in the path /auth/callback the session starts correctly and does not remain in an infinite loop of redirections.


Solution

  • The problem was due to the sameSite: 'strict'.

    The domain of my external login is not the same of my site, so the cookie was not accessible. The requests to my callback came from an external domain and the redirection was to an internal domain, so the cookie did not exist in that context.

    I was able to fix this by using the rule sameSite: 'lax'