web-player: fix open redirect via returnTo, require SESSION_SECRET and validate API inputs

This commit is contained in:
2026-09-24 04:58:08 +02:00
parent b2f450dcf7
commit 3e546d4a1e
12 changed files with 64 additions and 13 deletions
+6 -2
View File
@@ -5,9 +5,13 @@ import { createApiClient } from '$lib/server/api';
export const POST: RequestHandler = async ({ request, locals, fetch }) => {
if (!locals.accessToken) return json({ message: 'Inicia sesión.' }, { status: 401 });
const body = await request.json();
const payload = await request.json().catch(() => null);
const trackId = payload?.trackId;
if (typeof trackId !== 'string' || trackId === '')
return json({ message: 'Canción inválida.' }, { status: 400 });
const api = createApiClient({ fetch, accessToken: locals.accessToken });
const result = await api.POST('/likes/toggle', { body });
const result = await api.POST('/likes/toggle', { body: { trackId } });
if (result.error || result.data === undefined)
return json({ message: 'No se pudo actualizar Me gusta.' }, { status: 502 });
@@ -5,7 +5,8 @@ import { createApiClient } from '$lib/server/api';
export const PUT: RequestHandler = async ({ params, request, locals, fetch }) => {
if (!locals.accessToken) return json({ message: 'Inicia sesión.' }, { status: 401 });
const { playedSeconds } = (await request.json()) as { playedSeconds?: number };
const payload = (await request.json().catch(() => null)) as { playedSeconds?: unknown } | null;
const playedSeconds = payload?.playedSeconds;
if (typeof playedSeconds !== 'number' || !Number.isFinite(playedSeconds) || playedSeconds < 0)
return json({ message: 'Tiempo inválido.' }, { status: 400 });
+5 -2
View File
@@ -1,13 +1,16 @@
import type { RequestHandler } from './$types';
import { json } from '@sveltejs/kit';
import { createApiClient, unwrapOrError } from '$lib/server/api';
import { EXPLORE_PAGE_SIZE } from '$lib/config';
import { EXPLORE_PAGE_SIZE, MAX_PAGE_SIZE } from '$lib/config';
export const GET: RequestHandler = async ({ url, locals, fetch }) => {
const name = url.searchParams.get('name')?.trim() || undefined;
const genre = url.searchParams.get('genre')?.trim() || undefined;
const pageNumber = Math.max(1, Number(url.searchParams.get('pageNumber')) || 1);
const pageSize = Math.max(1, Number(url.searchParams.get('pageSize')) || EXPLORE_PAGE_SIZE);
const pageSize = Math.min(
MAX_PAGE_SIZE,
Math.max(1, Number(url.searchParams.get('pageSize')) || EXPLORE_PAGE_SIZE)
);
const api = createApiClient({ fetch, accessToken: locals.accessToken ?? undefined });
const result = await api.GET('/tracks', {
+4 -2
View File
@@ -1,9 +1,11 @@
import { redirect } from '@sveltejs/kit';
import type { PageServerLoad } from './$types';
import { safeReturnTo } from '$lib/server/auth';
export const load: PageServerLoad = ({ locals, url }) => {
const returnTo = safeReturnTo(url.searchParams.get('returnTo'));
if (locals.user) {
redirect(302, url.searchParams.get('returnTo') ?? '/');
redirect(302, returnTo);
}
return { returnTo: url.searchParams.get('returnTo') ?? '/' };
return { returnTo };
};
@@ -5,6 +5,7 @@ import {
getOidcConfig,
encodeSession,
toSessionUser,
safeReturnTo,
writeSessionCookie,
type Session,
STATE_COOKIE,
@@ -17,7 +18,7 @@ export const GET: RequestHandler = async ({ url, cookies }) => {
const codeVerifier = cookies.get(VERIFIER_COOKIE);
const state = cookies.get(STATE_COOKIE);
const nonce = cookies.get(NONCE_COOKIE);
const returnTo = cookies.get(RETURN_COOKIE) ?? '/';
const returnTo = safeReturnTo(cookies.get(RETURN_COOKIE));
if (!codeVerifier || !state) {
error(400, 'Estado de autenticación inválido. Vuelve a iniciar sesión.');
+2 -1
View File
@@ -3,6 +3,7 @@ import * as client from 'openid-client';
import type { RequestHandler } from './$types';
import {
getOidcConfig,
safeReturnTo,
STATE_COOKIE,
VERIFIER_COOKIE,
NONCE_COOKIE,
@@ -19,7 +20,7 @@ export const GET: RequestHandler = async ({ url, cookies }) => {
const nonce = client.randomNonce();
const register = url.searchParams.get('mode') === 'register';
const returnTo = url.searchParams.get('returnTo') ?? '/';
const returnTo = safeReturnTo(url.searchParams.get('returnTo'));
const parameters: Record<string, string> = {
redirect_uri: authConfig.redirectUri,