fix(auth): resolve hardcoded localhost in user-settings across all web apps

The createUserSettingsStore was receiving a static auth URL evaluated at
module load time, before window.__PUBLIC_MANA_CORE_AUTH_URL__ was
injected by hooks.server.ts. In production this caused CSP violations
as settings API calls went to localhost:3001 instead of auth.mana.how.

Changes:
- Accept string | (() => string) for authUrl in shared-theme config
- Resolve authUrl lazily at fetch time instead of module load
- Fix fallback to empty string in non-dev environments (was localhost)
- Pass getAuthUrl as getter function in all 17 web apps

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-03-25 12:40:30 +01:00
parent 1fe8f8902d
commit 3376b044bc
19 changed files with 92 additions and 50 deletions

View file

@ -16,13 +16,13 @@ function getAuthUrl(): string {
if (browser && typeof window !== 'undefined') { if (browser && typeof window !== 'undefined') {
const injectedUrl = (window as unknown as { __PUBLIC_MANA_CORE_AUTH_URL__?: string }) const injectedUrl = (window as unknown as { __PUBLIC_MANA_CORE_AUTH_URL__?: string })
.__PUBLIC_MANA_CORE_AUTH_URL__; .__PUBLIC_MANA_CORE_AUTH_URL__;
return injectedUrl || 'http://localhost:3001'; if (injectedUrl) return injectedUrl;
} }
return 'http://localhost:3001'; return import.meta.env.DEV ? 'http://localhost:3001' : '';
} }
export const userSettings = createUserSettingsStore({ export const userSettings = createUserSettingsStore({
appId: 'calendar', appId: 'calendar',
authUrl: getAuthUrl(), authUrl: getAuthUrl,
getAccessToken: () => authStore.getAccessToken(), getAccessToken: () => authStore.getAccessToken(),
}); });

View file

@ -7,13 +7,21 @@
* - localStorage caching for offline support * - localStorage caching for offline support
*/ */
import { browser } from '$app/environment';
import { createUserSettingsStore } from '@manacore/shared-theme'; import { createUserSettingsStore } from '@manacore/shared-theme';
import { authStore } from './auth.svelte'; import { authStore } from './auth.svelte';
const MANA_AUTH_URL = 'http://localhost:3001'; function getAuthUrl(): string {
if (browser && typeof window !== 'undefined') {
const injectedUrl = (window as unknown as { __PUBLIC_MANA_CORE_AUTH_URL__?: string })
.__PUBLIC_MANA_CORE_AUTH_URL__;
if (injectedUrl) return injectedUrl;
}
return import.meta.env.DEV ? 'http://localhost:3001' : '';
}
export const userSettings = createUserSettingsStore({ export const userSettings = createUserSettingsStore({
appId: 'chat', appId: 'chat',
authUrl: MANA_AUTH_URL, authUrl: getAuthUrl,
getAccessToken: () => authStore.getAccessToken(), getAccessToken: () => authStore.getAccessToken(),
}); });

View file

@ -16,13 +16,13 @@ function getAuthUrl(): string {
if (browser && typeof window !== 'undefined') { if (browser && typeof window !== 'undefined') {
const injectedUrl = (window as unknown as { __PUBLIC_MANA_CORE_AUTH_URL__?: string }) const injectedUrl = (window as unknown as { __PUBLIC_MANA_CORE_AUTH_URL__?: string })
.__PUBLIC_MANA_CORE_AUTH_URL__; .__PUBLIC_MANA_CORE_AUTH_URL__;
return injectedUrl || 'http://localhost:3001'; if (injectedUrl) return injectedUrl;
} }
return 'http://localhost:3001'; return import.meta.env.DEV ? 'http://localhost:3001' : '';
} }
export const userSettings = createUserSettingsStore({ export const userSettings = createUserSettingsStore({
appId: 'clock', appId: 'clock',
authUrl: getAuthUrl(), authUrl: getAuthUrl,
getAccessToken: () => authStore.getAccessToken(), getAccessToken: () => authStore.getAccessToken(),
}); });

View file

@ -16,13 +16,13 @@ function getAuthUrl(): string {
if (browser && typeof window !== 'undefined') { if (browser && typeof window !== 'undefined') {
const injectedUrl = (window as unknown as { __PUBLIC_MANA_CORE_AUTH_URL__?: string }) const injectedUrl = (window as unknown as { __PUBLIC_MANA_CORE_AUTH_URL__?: string })
.__PUBLIC_MANA_CORE_AUTH_URL__; .__PUBLIC_MANA_CORE_AUTH_URL__;
return injectedUrl || 'http://localhost:3001'; if (injectedUrl) return injectedUrl;
} }
return 'http://localhost:3001'; return import.meta.env.DEV ? 'http://localhost:3001' : '';
} }
export const userSettings = createUserSettingsStore({ export const userSettings = createUserSettingsStore({
appId: 'contacts', appId: 'contacts',
authUrl: getAuthUrl(), authUrl: getAuthUrl,
getAccessToken: () => authStore.getAccessToken(), getAccessToken: () => authStore.getAccessToken(),
}); });

View file

@ -6,13 +6,13 @@ function getAuthUrl(): string {
if (browser && typeof window !== 'undefined') { if (browser && typeof window !== 'undefined') {
const injectedUrl = (window as unknown as { __PUBLIC_MANA_CORE_AUTH_URL__?: string }) const injectedUrl = (window as unknown as { __PUBLIC_MANA_CORE_AUTH_URL__?: string })
.__PUBLIC_MANA_CORE_AUTH_URL__; .__PUBLIC_MANA_CORE_AUTH_URL__;
return injectedUrl || 'http://localhost:3001'; if (injectedUrl) return injectedUrl;
} }
return 'http://localhost:3001'; return import.meta.env.DEV ? 'http://localhost:3001' : '';
} }
export const userSettings = createUserSettingsStore({ export const userSettings = createUserSettingsStore({
appId: 'context', appId: 'context',
authUrl: getAuthUrl(), authUrl: getAuthUrl,
getAccessToken: () => authStore.getAccessToken(), getAccessToken: () => authStore.getAccessToken(),
}); });

View file

@ -16,13 +16,13 @@ function getAuthUrl(): string {
if (browser && typeof window !== 'undefined') { if (browser && typeof window !== 'undefined') {
const injectedUrl = (window as unknown as { __PUBLIC_MANA_CORE_AUTH_URL__?: string }) const injectedUrl = (window as unknown as { __PUBLIC_MANA_CORE_AUTH_URL__?: string })
.__PUBLIC_MANA_CORE_AUTH_URL__; .__PUBLIC_MANA_CORE_AUTH_URL__;
return injectedUrl || 'http://localhost:3001'; if (injectedUrl) return injectedUrl;
} }
return 'http://localhost:3001'; return import.meta.env.DEV ? 'http://localhost:3001' : '';
} }
export const userSettings = createUserSettingsStore({ export const userSettings = createUserSettingsStore({
appId: 'manacore', appId: 'manacore',
authUrl: getAuthUrl(), authUrl: getAuthUrl,
getAccessToken: () => authStore.getAccessToken(), getAccessToken: () => authStore.getAccessToken(),
}); });

View file

@ -7,13 +7,21 @@
* - localStorage caching for offline support * - localStorage caching for offline support
*/ */
import { browser } from '$app/environment';
import { createUserSettingsStore } from '@manacore/shared-theme'; import { createUserSettingsStore } from '@manacore/shared-theme';
import { authStore } from './auth.svelte'; import { authStore } from './auth.svelte';
const MANA_AUTH_URL = 'http://localhost:3001'; function getAuthUrl(): string {
if (browser && typeof window !== 'undefined') {
const injectedUrl = (window as unknown as { __PUBLIC_MANA_CORE_AUTH_URL__?: string })
.__PUBLIC_MANA_CORE_AUTH_URL__;
if (injectedUrl) return injectedUrl;
}
return import.meta.env.DEV ? 'http://localhost:3001' : '';
}
export const userSettings = createUserSettingsStore({ export const userSettings = createUserSettingsStore({
appId: 'manadeck', appId: 'manadeck',
authUrl: MANA_AUTH_URL, authUrl: getAuthUrl,
getAccessToken: () => authStore.getAccessToken(), getAccessToken: () => authStore.getAccessToken(),
}); });

View file

@ -6,13 +6,13 @@ function getAuthUrl(): string {
if (browser && typeof window !== 'undefined') { if (browser && typeof window !== 'undefined') {
const injectedUrl = (window as unknown as { __PUBLIC_MANA_CORE_AUTH_URL__?: string }) const injectedUrl = (window as unknown as { __PUBLIC_MANA_CORE_AUTH_URL__?: string })
.__PUBLIC_MANA_CORE_AUTH_URL__; .__PUBLIC_MANA_CORE_AUTH_URL__;
return injectedUrl || 'http://localhost:3001'; if (injectedUrl) return injectedUrl;
} }
return 'http://localhost:3001'; return import.meta.env.DEV ? 'http://localhost:3001' : '';
} }
export const userSettings = createUserSettingsStore({ export const userSettings = createUserSettingsStore({
appId: 'mukke', appId: 'mukke',
authUrl: getAuthUrl(), authUrl: getAuthUrl,
getAccessToken: () => authStore.getValidToken(), getAccessToken: () => authStore.getValidToken(),
}); });

View file

@ -6,13 +6,13 @@ function getAuthUrl(): string {
if (browser && typeof window !== 'undefined') { if (browser && typeof window !== 'undefined') {
const injectedUrl = (window as unknown as { __PUBLIC_MANA_CORE_AUTH_URL__?: string }) const injectedUrl = (window as unknown as { __PUBLIC_MANA_CORE_AUTH_URL__?: string })
.__PUBLIC_MANA_CORE_AUTH_URL__; .__PUBLIC_MANA_CORE_AUTH_URL__;
return injectedUrl || 'http://localhost:3001'; if (injectedUrl) return injectedUrl;
} }
return 'http://localhost:3001'; return import.meta.env.DEV ? 'http://localhost:3001' : '';
} }
export const userSettings = createUserSettingsStore({ export const userSettings = createUserSettingsStore({
appId: 'photos', appId: 'photos',
authUrl: getAuthUrl(), authUrl: getAuthUrl,
getAccessToken: () => authStore.getAccessToken(), getAccessToken: () => authStore.getAccessToken(),
}); });

View file

@ -7,13 +7,21 @@
* - localStorage caching for offline support * - localStorage caching for offline support
*/ */
import { browser } from '$app/environment';
import { createUserSettingsStore } from '@manacore/shared-theme'; import { createUserSettingsStore } from '@manacore/shared-theme';
import { authStore } from './auth.svelte'; import { authStore } from './auth.svelte';
const MANA_AUTH_URL = 'http://localhost:3001'; function getAuthUrl(): string {
if (browser && typeof window !== 'undefined') {
const injectedUrl = (window as unknown as { __PUBLIC_MANA_CORE_AUTH_URL__?: string })
.__PUBLIC_MANA_CORE_AUTH_URL__;
if (injectedUrl) return injectedUrl;
}
return import.meta.env.DEV ? 'http://localhost:3001' : '';
}
export const userSettings = createUserSettingsStore({ export const userSettings = createUserSettingsStore({
appId: 'picture', appId: 'picture',
authUrl: MANA_AUTH_URL, authUrl: getAuthUrl,
getAccessToken: () => authStore.getAccessToken(), getAccessToken: () => authStore.getAccessToken(),
}); });

View file

@ -6,13 +6,13 @@ function getAuthUrl(): string {
if (browser && typeof window !== 'undefined') { if (browser && typeof window !== 'undefined') {
const injectedUrl = (window as unknown as { __PUBLIC_MANA_CORE_AUTH_URL__?: string }) const injectedUrl = (window as unknown as { __PUBLIC_MANA_CORE_AUTH_URL__?: string })
.__PUBLIC_MANA_CORE_AUTH_URL__; .__PUBLIC_MANA_CORE_AUTH_URL__;
return injectedUrl || 'http://localhost:3001'; if (injectedUrl) return injectedUrl;
} }
return 'http://localhost:3001'; return import.meta.env.DEV ? 'http://localhost:3001' : '';
} }
export const userSettings = createUserSettingsStore({ export const userSettings = createUserSettingsStore({
appId: 'planta', appId: 'planta',
authUrl: getAuthUrl(), authUrl: getAuthUrl,
getAccessToken: () => authStore.getValidToken(), getAccessToken: () => authStore.getValidToken(),
}); });

View file

@ -7,13 +7,21 @@
* - localStorage caching for offline support * - localStorage caching for offline support
*/ */
import { browser } from '$app/environment';
import { createUserSettingsStore } from '@manacore/shared-theme'; import { createUserSettingsStore } from '@manacore/shared-theme';
import { auth } from './auth.svelte'; import { auth } from './auth.svelte';
const MANA_AUTH_URL = 'http://localhost:3001'; function getAuthUrl(): string {
if (browser && typeof window !== 'undefined') {
const injectedUrl = (window as unknown as { __PUBLIC_MANA_CORE_AUTH_URL__?: string })
.__PUBLIC_MANA_CORE_AUTH_URL__;
if (injectedUrl) return injectedUrl;
}
return import.meta.env.DEV ? 'http://localhost:3001' : '';
}
export const userSettings = createUserSettingsStore({ export const userSettings = createUserSettingsStore({
appId: 'presi', appId: 'presi',
authUrl: MANA_AUTH_URL, authUrl: getAuthUrl,
getAccessToken: () => auth.getAccessToken(), getAccessToken: () => auth.getAccessToken(),
}); });

View file

@ -6,13 +6,13 @@ function getAuthUrl(): string {
if (browser && typeof window !== 'undefined') { if (browser && typeof window !== 'undefined') {
const injectedUrl = (window as unknown as { __PUBLIC_MANA_CORE_AUTH_URL__?: string }) const injectedUrl = (window as unknown as { __PUBLIC_MANA_CORE_AUTH_URL__?: string })
.__PUBLIC_MANA_CORE_AUTH_URL__; .__PUBLIC_MANA_CORE_AUTH_URL__;
return injectedUrl || 'http://localhost:3001'; if (injectedUrl) return injectedUrl;
} }
return 'http://localhost:3001'; return import.meta.env.DEV ? 'http://localhost:3001' : '';
} }
export const userSettings = createUserSettingsStore({ export const userSettings = createUserSettingsStore({
appId: 'questions', appId: 'questions',
authUrl: getAuthUrl(), authUrl: getAuthUrl,
getAccessToken: () => authStore.getValidToken(), getAccessToken: () => authStore.getValidToken(),
}); });

View file

@ -6,13 +6,13 @@ function getAuthUrl(): string {
if (browser && typeof window !== 'undefined') { if (browser && typeof window !== 'undefined') {
const injectedUrl = (window as unknown as { __PUBLIC_MANA_CORE_AUTH_URL__?: string }) const injectedUrl = (window as unknown as { __PUBLIC_MANA_CORE_AUTH_URL__?: string })
.__PUBLIC_MANA_CORE_AUTH_URL__; .__PUBLIC_MANA_CORE_AUTH_URL__;
return injectedUrl || 'http://localhost:3001'; if (injectedUrl) return injectedUrl;
} }
return 'http://localhost:3001'; return import.meta.env.DEV ? 'http://localhost:3001' : '';
} }
export const userSettings = createUserSettingsStore({ export const userSettings = createUserSettingsStore({
appId: 'skilltree', appId: 'skilltree',
authUrl: getAuthUrl(), authUrl: getAuthUrl,
getAccessToken: () => authStore.getAccessToken(), getAccessToken: () => authStore.getAccessToken(),
}); });

View file

@ -7,13 +7,21 @@
* - localStorage caching for offline support * - localStorage caching for offline support
*/ */
import { browser } from '$app/environment';
import { createUserSettingsStore } from '@manacore/shared-theme'; import { createUserSettingsStore } from '@manacore/shared-theme';
import { authStore } from './auth.svelte'; import { authStore } from './auth.svelte';
const MANA_AUTH_URL = 'http://localhost:3001'; function getAuthUrl(): string {
if (browser && typeof window !== 'undefined') {
const injectedUrl = (window as unknown as { __PUBLIC_MANA_CORE_AUTH_URL__?: string })
.__PUBLIC_MANA_CORE_AUTH_URL__;
if (injectedUrl) return injectedUrl;
}
return import.meta.env.DEV ? 'http://localhost:3001' : '';
}
export const userSettings = createUserSettingsStore({ export const userSettings = createUserSettingsStore({
appId: 'storage', appId: 'storage',
authUrl: MANA_AUTH_URL, authUrl: getAuthUrl,
getAccessToken: () => authStore.getAccessToken(), getAccessToken: () => authStore.getAccessToken(),
}); });

View file

@ -7,13 +7,13 @@ function getAuthUrl(): string {
if (browser && typeof window !== 'undefined') { if (browser && typeof window !== 'undefined') {
const injectedUrl = (window as unknown as { __PUBLIC_MANA_CORE_AUTH_URL__?: string }) const injectedUrl = (window as unknown as { __PUBLIC_MANA_CORE_AUTH_URL__?: string })
.__PUBLIC_MANA_CORE_AUTH_URL__; .__PUBLIC_MANA_CORE_AUTH_URL__;
return injectedUrl || 'http://localhost:3001'; if (injectedUrl) return injectedUrl;
} }
return 'http://localhost:3001'; return import.meta.env.DEV ? 'http://localhost:3001' : '';
} }
export const userSettings = createUserSettingsStore({ export const userSettings = createUserSettingsStore({
appId: 'todo', appId: 'todo',
authUrl: getAuthUrl(), authUrl: getAuthUrl,
getAccessToken: () => authStore.getAccessToken(), getAccessToken: () => authStore.getAccessToken(),
}); });

View file

@ -6,13 +6,13 @@ function getAuthUrl(): string {
if (browser && typeof window !== 'undefined') { if (browser && typeof window !== 'undefined') {
const injectedUrl = (window as unknown as { __PUBLIC_MANA_CORE_AUTH_URL__?: string }) const injectedUrl = (window as unknown as { __PUBLIC_MANA_CORE_AUTH_URL__?: string })
.__PUBLIC_MANA_CORE_AUTH_URL__; .__PUBLIC_MANA_CORE_AUTH_URL__;
return injectedUrl || 'http://localhost:3001'; if (injectedUrl) return injectedUrl;
} }
return 'http://localhost:3001'; return import.meta.env.DEV ? 'http://localhost:3001' : '';
} }
export const userSettings = createUserSettingsStore({ export const userSettings = createUserSettingsStore({
appId: 'zitare', appId: 'zitare',
authUrl: getAuthUrl(), authUrl: getAuthUrl,
getAccessToken: () => authStore.getAccessToken(), getAccessToken: () => authStore.getAccessToken(),
}); });

View file

@ -431,8 +431,8 @@ export interface UserSettingsStore {
export interface UserSettingsStoreConfig { export interface UserSettingsStoreConfig {
/** App identifier (e.g., 'calendar', 'chat') */ /** App identifier (e.g., 'calendar', 'chat') */
appId: string; appId: string;
/** Auth service base URL */ /** Auth service base URL (string or getter function for lazy resolution) */
authUrl: string; authUrl: string | (() => string);
/** Function to get current access token */ /** Function to get current access token */
getAccessToken: () => Promise<string | null>; getAccessToken: () => Promise<string | null>;
/** Optional device name (auto-detected if not provided) */ /** Optional device name (auto-detected if not provided) */

View file

@ -103,7 +103,9 @@ function detectDeviceName(): string {
* ``` * ```
*/ */
export function createUserSettingsStore(config: UserSettingsStoreConfig): UserSettingsStore { export function createUserSettingsStore(config: UserSettingsStoreConfig): UserSettingsStore {
const { appId, authUrl, getAccessToken, deviceName, deviceType } = config; const { appId, authUrl: authUrlConfig, getAccessToken, deviceName, deviceType } = config;
const resolveAuthUrl = () =>
typeof authUrlConfig === 'function' ? authUrlConfig() : authUrlConfig;
const storageKey = `${STORAGE_KEY_PREFIX}-${appId}`; const storageKey = `${STORAGE_KEY_PREFIX}-${appId}`;
// Device info (initialized once) // Device info (initialized once)
@ -202,7 +204,7 @@ export function createUserSettingsStore(config: UserSettingsStoreConfig): UserSe
} }
try { try {
const response = await fetch(`${authUrl}/api/v1/settings${path}`, { const response = await fetch(`${resolveAuthUrl()}/api/v1/settings${path}`, {
method, method,
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',