diff --git a/.claude/guidelines/authentication.md b/.claude/guidelines/authentication.md index d0f9a5dae..34d9c3dda 100644 --- a/.claude/guidelines/authentication.md +++ b/.claude/guidelines/authentication.md @@ -202,7 +202,206 @@ APP_ID=your-app-id ## Client Integration (Web) -### Setup +### Runtime URL Injection (Recommended for Docker) + +For Docker deployments, use runtime URL injection instead of build-time environment variables: + +**Step 1: Server Hook (`hooks.server.ts`)** + +```typescript +// src/hooks.server.ts +import type { Handle } from '@sveltejs/kit'; +import { env } from '$env/dynamic/private'; + +export const handle: Handle = async ({ event, resolve }) => { + return resolve(event, { + transformPageChunk: ({ html }) => + html.replace( + '%RUNTIME_ENV%', + `` + ), + }); +}; +``` + +**Step 2: Update `app.html`** + +```html + + + + %sveltekit.head% + %RUNTIME_ENV% + + +
%sveltekit.body%
+ + +``` + +**Step 3: URL Helper (`url.ts`)** + +```typescript +// src/lib/config/url.ts +import { browser } from '$app/environment'; +import { PUBLIC_MANA_CORE_AUTH_URL, PUBLIC_BACKEND_URL } from '$env/static/public'; + +declare global { + interface Window { + __PUBLIC_AUTH_URL__?: string; + __PUBLIC_BACKEND_URL__?: string; + } +} + +export function getAuthUrl(): string { + if (browser && window.__PUBLIC_AUTH_URL__) { + return window.__PUBLIC_AUTH_URL__; + } + return PUBLIC_MANA_CORE_AUTH_URL || 'http://localhost:3001'; +} + +export function getBackendUrl(): string { + if (browser && window.__PUBLIC_BACKEND_URL__) { + return window.__PUBLIC_BACKEND_URL__; + } + return PUBLIC_BACKEND_URL || 'http://localhost:3000'; +} +``` + +### Standard Auth Store Pattern (Svelte 5) + +```typescript +// src/lib/stores/auth.svelte.ts +import { browser } from '$app/environment'; +import { goto } from '$app/navigation'; +import { initializeWebAuth } from '@manacore/shared-auth'; +import { getAuthUrl } from '$lib/config/url'; + +// Lazy initialization - only create when needed (browser only) +let _authService: ReturnType['authService'] | null = null; + +function getAuthService() { + if (!browser) return null; + if (!_authService) { + const auth = initializeWebAuth({ baseUrl: getAuthUrl() }); + _authService = auth.authService; + } + return _authService; +} + +// Svelte 5 reactive state +let user = $state(null); +let accessToken = $state(null); +let loading = $state(true); +let initialized = $state(false); + +// Initialize auth on app start +async function initialize() { + if (!browser || initialized) return; + + const authService = getAuthService(); + if (!authService) { + loading = false; + return; + } + + try { + const currentUser = await authService.getCurrentUser(); + if (currentUser) { + user = currentUser; + // Use getValidToken() for auto-refresh, NOT getAccessToken() + accessToken = await authService.getValidToken(); + } + } catch (error) { + console.error('Auth initialization failed:', error); + user = null; + accessToken = null; + } finally { + loading = false; + initialized = true; + } +} + +// Get a valid token (with auto-refresh if expired) +async function getValidToken(): Promise { + const authService = getAuthService(); + if (!authService) return null; + + try { + return await authService.getValidToken(); + } catch { + // Token refresh failed, user needs to re-login + await logout(); + return null; + } +} + +// DEPRECATED: Use getValidToken() instead +function getAccessToken(): string | null { + console.warn('getAccessToken() is deprecated. Use getValidToken() for auto-refresh.'); + return accessToken; +} + +async function login(email: string, password: string): Promise { + const authService = getAuthService(); + if (!authService) return false; + + try { + const result = await authService.signIn({ email, password }); + user = result.user; + accessToken = result.accessToken; + return true; + } catch { + return false; + } +} + +async function logout() { + const authService = getAuthService(); + if (authService) { + try { + await authService.signOut(); + } catch { + // Ignore logout errors + } + } + user = null; + accessToken = null; + goto('/login'); +} + +export const authStore = { + // Getters (reactive) + get user() { return user; }, + get loading() { return loading; }, + get isAuthenticated() { return !!accessToken && !!user; }, + get initialized() { return initialized; }, + + // Methods + initialize, + login, + logout, + getValidToken, // RECOMMENDED + getAccessToken, // DEPRECATED +}; +``` + +### Key Best Practices + +| Practice | Description | +|----------|-------------| +| **Lazy Initialization** | Only create auth service when needed (browser check) | +| **Use `getValidToken()`** | Auto-refreshes expired tokens, unlike `getAccessToken()` | +| **Runtime URL Injection** | Enables Docker deployments without rebuild | +| **SSR Guard** | Always check `browser` before accessing auth service | +| **Initialized Flag** | Prevents double initialization | + +### Basic Setup (Static URLs) + +For simpler deployments without Docker, use static environment variables: ```typescript // src/lib/stores/auth.svelte.ts diff --git a/docker-compose.macmini.yml b/docker-compose.macmini.yml index baa70a8af..cbf9c42ee 100644 --- a/docker-compose.macmini.yml +++ b/docker-compose.macmini.yml @@ -1258,7 +1258,7 @@ services: condition: service_healthy environment: NODE_ENV: production - PORT: 3023 + PORT: 3033 TZ: Europe/Berlin MATRIX_HOMESERVER_URL: http://synapse:8008 MATRIX_ACCESS_TOKEN: ${MATRIX_TTS_BOT_TOKEN} @@ -1270,9 +1270,9 @@ services: volumes: - matrix_tts_bot_data:/app/data ports: - - "3023:3023" + - "3033:3033" healthcheck: - test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:3023/health"] + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:3033/health"] interval: 30s timeout: 10s retries: 3 diff --git a/docs/CONSISTENCY_REPORT.md b/docs/CONSISTENCY_REPORT.md index daf947ed0..50888ec13 100644 --- a/docs/CONSISTENCY_REPORT.md +++ b/docs/CONSISTENCY_REPORT.md @@ -13,7 +13,7 @@ Nach eingehender Analyse aller Web-Apps im Monorepo wurden folgende Bereiche auf | Toast System | ✅ Gut | ~~Hoch~~ | ✅ Erledigt | | API Client Patterns | ✅ Gut | ~~Hoch~~ | ✅ Erledigt | | i18n Implementation | ✅ Gut | ~~Mittel~~ | ✅ Erledigt | -| Auth Implementation | ✅ Gut | Niedrig | - | +| Auth Implementation | ✅ Gut | ~~Niedrig~~ | ✅ Dokumentiert | | Styling & Tailwind | ✅ Sehr gut | Niedrig | - | | Komponenten & Layouts | ✅ Gut | ~~Mittel~~ | ✅ Erledigt | @@ -28,6 +28,7 @@ Nach eingehender Analyse aller Web-Apps im Monorepo wurden folgende Bereiche auf 7. ✅ **AuthGateModal zentralisiert** - `@manacore/shared-auth-ui` für 4 Apps (chat, todo, contacts, calendar) 8. ✅ **Global Error Handler zentralisiert** - `@manacore/shared-ui` für 7 Apps (calendar, chat, clock, contacts, matrix, picture, storage) 9. ✅ **AppLoadingSkeleton zentralisiert** - `@manacore/shared-ui` für 3 Apps (contacts, todo, questions) - Apps mit spezifischen Layouts (calendar, clock) behalten lokale Version +10. ✅ **Auth Store Pattern dokumentiert** - `.claude/guidelines/authentication.md` erweitert mit Runtime URL Injection, getValidToken(), Best Practices --- @@ -268,6 +269,7 @@ Alle Apps nutzen **Mana Core Auth** mit `@manacore/shared-auth`. | ~~i18n zu 6 Apps hinzufügen~~ | ✅ Erledigt | | ~~AuthGateModal zentralisieren~~ | ✅ Erledigt (4 Apps migriert) | | ~~Global Error Handler extrahieren~~ | ✅ Erledigt (7 Apps migriert) | +| ~~Auth Store Pattern dokumentieren~~ | ✅ Erledigt | ### 🔴 Hohe Priorität @@ -282,7 +284,7 @@ _(Keine offenen Aufgaben mit mittlerer Priorität)_ | Aufgabe | Aufwand | Impact | |---------|---------|--------| | ~~App-Skeletons vereinheitlichen~~ | ~~Niedrig~~ | ✅ Erledigt | -| Auth Store Pattern dokumentieren | Niedrig | Onboarding | +| ~~Auth Store Pattern dokumentieren~~ | ~~Niedrig~~ | ✅ Erledigt | --- @@ -293,7 +295,7 @@ _(Keine offenen Aufgaben mit mittlerer Priorität)_ 3. ~~**AuthGateModal** in Shared Package extrahieren~~ ✅ Erledigt (4 Apps) 4. ~~**Global Error Handler** extrahieren~~ ✅ Erledigt (7 Apps) 5. ~~**App-Skeletons vereinheitlichen**~~ ✅ Erledigt (3 Apps) -6. **Auth Store Pattern dokumentieren** (niedrige Priorität) +6. ~~**Auth Store Pattern dokumentieren**~~ ✅ Erledigt ---