feat(app) : socle SvelteKit phase 0 — SSO, thème OKI, adapter-node
- Scaffold SvelteKit 2 + Svelte 5 (runes), TypeScript strict, adapter-node - Auth SSO via header Ynh-User (hooks.server) ; accueil public minimal sans header, 401 sur toute autre route ; DEV_USER en dev local uniquement - Design system OKI : tokens, thème sombre par défaut, clair opt-in anti-FOUC, flag-bar, fonts Archivo/Inter self-hébergées, sprite icons.svg (zéro emoji) - CSP via kit.csp (mode nonce) + en-têtes de sécurité ; paths.base configurable au build (BASE_PATH) pour le sous-chemin YunoHost, paths.relative = false - i18n fr (structure prête gcf/en), pages : accueil public/privé, erreurs designées - Tests vitest : résolution SSO + routes publiques (10 cas) - Vérifié : check/lint/test/build verts, test fumée HTTP sur build (SSO, CSP, sous-chemin /veille), zéro emoji et zéro domaine tiers dans le build
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
import { dev } from '$app/environment';
|
||||
import { base } from '$app/paths';
|
||||
import { sequence } from '@sveltejs/kit/hooks';
|
||||
import { error, type Handle } from '@sveltejs/kit';
|
||||
import { config } from '$lib/server/config';
|
||||
import { estRoutePublique, resoudreUtilisateur } from '$lib/server/sso';
|
||||
|
||||
/**
|
||||
* Authentification SSO : l'utilisateur vient du header injecté par le reverse proxy
|
||||
* YunoHost (SSOwat). Sans utilisateur, seule la page d'accueil publique est accessible ;
|
||||
* toute autre route (pages et endpoints API) renvoie 401.
|
||||
*/
|
||||
const authHandle: Handle = async ({ event, resolve }) => {
|
||||
event.locals.user = resoudreUtilisateur(event.request.headers, {
|
||||
ssoHeader: config.ssoHeader,
|
||||
devUser: config.devUser,
|
||||
dev
|
||||
});
|
||||
|
||||
if (!event.locals.user && !estRoutePublique(event.url.pathname, base)) {
|
||||
error(401, 'Authentification requise');
|
||||
}
|
||||
|
||||
return resolve(event);
|
||||
};
|
||||
|
||||
/** En-têtes de sécurité complétant la CSP (gérée par la config SvelteKit, mode nonce). */
|
||||
const securityHeadersHandle: Handle = async ({ event, resolve }) => {
|
||||
const response = await resolve(event);
|
||||
response.headers.set('X-Content-Type-Options', 'nosniff');
|
||||
response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
|
||||
response.headers.set('Permissions-Policy', 'camera=(), microphone=(), geolocation=()');
|
||||
return response;
|
||||
};
|
||||
|
||||
export const handle = sequence(authHandle, securityHeadersHandle);
|
||||
Reference in New Issue
Block a user