|
1 | 1 | /** |
2 | 2 | * |
3 | | - * Copyright © 2025 Ping Identity Corporation. All right reserved. |
| 3 | + * Copyright © 2025-2026 Ping Identity Corporation. All right reserved. |
4 | 4 | * |
5 | 5 | * This software may be modified and distributed under the terms |
6 | 6 | * of the MIT license. See the LICENSE file for details. |
7 | 7 | * |
8 | 8 | **/ |
9 | 9 |
|
10 | | -import type { RequestEvent } from '@sveltejs/kit'; |
11 | 10 | import type { PageServerLoad } from './$types'; |
12 | | -import type { z } from 'zod'; |
13 | | - |
14 | | -import { getLocale } from '$core/_utilities/i18n.utilities'; |
15 | | - |
16 | | -import type { stringsSchema } from '$core/locale.store'; |
17 | | - |
18 | | -export const load: PageServerLoad = async (event: RequestEvent) => { |
19 | | - const userLocale = event.request.headers.get('accept-language') || 'en-US'; |
20 | | - const locale = getLocale(userLocale, '/'); |
21 | | - const [country, lang] = locale.split('/'); |
22 | | - |
23 | | - let localeContent: { default: z.infer<typeof stringsSchema> }; |
24 | | - |
25 | | - try { |
26 | | - localeContent = await import(`$locales/${country}/${lang}/index.json`); |
27 | | - } catch (err) { |
28 | | - console.error(`User locale content for ${userLocale} was not found.`); |
29 | | - |
30 | | - // TODO: Reevaluate use of JS versus JSON without breaking type generation for lib |
31 | | - // eslint-disable-next-line |
32 | | - // @ts-ignore |
33 | | - localeContent = await import(`$locales/us/en/index.json`); |
| 11 | +import { loadLocaleContent } from '$server/locale'; |
| 12 | + |
| 13 | +import { REDIRECT_QUERY_PARAMS } from '$lib/redirect.constants'; |
| 14 | + |
| 15 | +export const load: PageServerLoad = async ({ request, url, cookies }) => { |
| 16 | + // https://docs.pingidentity.com/pingam/8/am-oauth2/oauth2-parameters.html#redirect-uri |
| 17 | + const redirectUri = url.searchParams.get('goto'); |
| 18 | + |
| 19 | + const userLocale = request.headers.get('accept-language') || 'en-US'; |
| 20 | + const content = await loadLocaleContent(userLocale); |
| 21 | + |
| 22 | + if (redirectUri) { |
| 23 | + try { |
| 24 | + // Normalize common inputs into something URL parsing + AM validateGoto can consistently handle. |
| 25 | + // Examples: |
| 26 | + // - '/path' stays relative (will be resolved against this app's origin below) |
| 27 | + // - 'example.com/path' becomes 'https://example.com/path' |
| 28 | + // - 'https://…' stays as-is |
| 29 | + const normalizedRedirectUri = |
| 30 | + redirectUri.startsWith('http://') || |
| 31 | + redirectUri.startsWith('https://') || |
| 32 | + redirectUri.startsWith('/') |
| 33 | + ? redirectUri |
| 34 | + : `https://${redirectUri}`; |
| 35 | + |
| 36 | + // Parse into a real URL object. If the input is relative (e.g. '/path'), use this app's origin |
| 37 | + // as the base so we end up with a full absolute URL in `parsed.href`. |
| 38 | + const parsed = new URL(normalizedRedirectUri, url.origin); |
| 39 | + |
| 40 | + // Only persist http(s) redirects. Other schemes like 'javascript:' are ignored. |
| 41 | + if (parsed.protocol === 'https:' || parsed.protocol === 'http:') { |
| 42 | + cookies.set(REDIRECT_QUERY_PARAMS, parsed.href, { |
| 43 | + httpOnly: true, |
| 44 | + sameSite: 'lax', |
| 45 | + secure: url.protocol === 'https:', |
| 46 | + maxAge: 300, |
| 47 | + path: '/', |
| 48 | + }); |
| 49 | + } |
| 50 | + } catch { |
| 51 | + // Ignore invalid redirectUri values |
| 52 | + } |
34 | 53 | } |
35 | 54 |
|
36 | | - return { content: localeContent.default }; |
| 55 | + return { content }; |
37 | 56 | }; |
0 commit comments