Authentifizierung mit serverseitigen Sitzungen, CSRF und Rollen
Passwoerter mit Argon2id, im Cookie steht nur ein Token und in der Datenbank dessen Hash. Das CSRF-Token wird per HMAC aus dem Sitzungstoken abgeleitet statt separat gespeichert - ohne das httpOnly-Cookie ist es nicht herstellbar. TOTP ist optional und wird erst nach einem bestaetigten Code scharf geschaltet, damit sich niemand bei der Einrichtung aussperrt.
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
const schema = z.object({
|
||||
NODE_ENV: z.enum(['development', 'test', 'production']).default('development'),
|
||||
PORT: z.coerce.number().int().positive().default(3000),
|
||||
HOST: z.string().default('0.0.0.0'),
|
||||
|
||||
DATABASE_URL: z.string().min(1, 'DATABASE_URL fehlt'),
|
||||
SESSION_SECRET: z.string().min(32, 'SESSION_SECRET muss mindestens 32 Zeichen lang sein'),
|
||||
|
||||
// Lebensdauer einer Session in Stunden.
|
||||
SESSION_TTL_HOURS: z.coerce.number().int().positive().default(12),
|
||||
|
||||
// Wird beim ersten Start angelegt, solange noch kein Account existiert.
|
||||
BOOTSTRAP_ADMIN_USER: z.string().min(3).default('admin'),
|
||||
BOOTSTRAP_ADMIN_PASSWORD: z.string().min(12).optional(),
|
||||
|
||||
// Hinter einem Reverse Proxy noetig, damit Client-IPs im Audit-Log stimmen.
|
||||
TRUST_PROXY: z
|
||||
.string()
|
||||
.default('true')
|
||||
.transform((v) => v === 'true' || v === '1'),
|
||||
|
||||
// Muss in Produktion true sein (Cookies nur ueber HTTPS).
|
||||
COOKIE_SECURE: z
|
||||
.string()
|
||||
.optional()
|
||||
.transform((v) => (v === undefined ? undefined : v === 'true' || v === '1')),
|
||||
|
||||
LOG_LEVEL: z.enum(['fatal', 'error', 'warn', 'info', 'debug', 'trace']).default('info'),
|
||||
});
|
||||
|
||||
const parsed = schema.safeParse(process.env);
|
||||
|
||||
if (!parsed.success) {
|
||||
const issues = parsed.error.issues.map((i) => ` - ${i.path.join('.')}: ${i.message}`).join('\n');
|
||||
console.error(`Konfiguration unvollstaendig:\n${issues}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const raw = parsed.data;
|
||||
|
||||
export const env = {
|
||||
...raw,
|
||||
isProduction: raw.NODE_ENV === 'production',
|
||||
// In Produktion ohne expliziten Wert: sichere Cookies erzwingen.
|
||||
cookieSecure: raw.COOKIE_SECURE ?? raw.NODE_ENV === 'production',
|
||||
};
|
||||
|
||||
export type Env = typeof env;
|
||||
Reference in New Issue
Block a user