feat(auth): auto-login after onboarding, consolidate session cookies
Lint & Test / lint-and-check (push) Failing after 5m1s
Lint & Test / test (push) Has been skipped

- Extract session cookie issuance into sessionCookies.ts helper; remove
  duplicated COOKIE_BASE blocks from login, register, oauth callback/authorize,
  refresh handler, hooks.server.ts, and onboarding.
- Derive cookie secure flag from ORIGIN (https://...) instead of NODE_ENV so
  plain-HTTP production deploys don't silently drop cookies.
- Auto-login admin after onboarding completes; UI does a full reload so
  hooks.server.ts picks up the new session.
- Harden onboarding: reject duplicate admin creation, flip onboardingComplete
  atomically to prevent concurrent completions, error out if no admin found.
- Fix Dockerfile CMD operator precedence: node build now always runs after
  migrate deploy || db push.
- Wire ORIGIN env through docker-compose.
This commit is contained in:
2026-04-16 03:28:46 +03:00
parent 2c9c36605d
commit 3fa30f72a3
11 changed files with 134 additions and 147 deletions
+6 -19
View File
@@ -7,6 +7,10 @@ import * as apiTokenService from '$lib/server/services/apiTokenService.js';
import { extractBearerToken } from '$lib/server/middleware/authenticate.js';
import { isBoardGuestAccessible } from '$lib/server/middleware/guestAccess.js';
import { initBackupScheduler } from '$lib/server/jobs/backupScheduler.js';
import {
clearSessionCookies,
setRotatedCookies
} from '$lib/server/utils/sessionCookies.js';
// Initialize backup scheduler on server startup
initBackupScheduler();
@@ -20,13 +24,6 @@ function isPublicPath(pathname: string): boolean {
const ACCESS_TOKEN_COOKIE = 'access_token';
const REFRESH_TOKEN_COOKIE = 'refresh_token';
const COOKIE_BASE = {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax' as const,
path: '/'
};
export const handle: Handle = async ({ event, resolve }) => {
// Initialize locals
event.locals.user = null;
@@ -68,15 +65,7 @@ export const handle: Handle = async ({ event, resolve }) => {
const user = await userService.findById(userIdFromCookie);
const tokens = await authService.rotateTokens(user.id, user.email, user.role);
// Set new cookies
event.cookies.set(ACCESS_TOKEN_COOKIE, tokens.accessToken, {
...COOKIE_BASE,
maxAge: 900 // 15 minutes
});
event.cookies.set(REFRESH_TOKEN_COOKIE, tokens.refreshToken, {
...COOKIE_BASE,
maxAge: 604800 // 7 days
});
setRotatedCookies(event.cookies, tokens);
event.locals.user = {
id: user.id,
@@ -92,9 +81,7 @@ export const handle: Handle = async ({ event, resolve }) => {
}
} catch {
// Refresh failed — clear stale cookies
event.cookies.delete(ACCESS_TOKEN_COOKIE, { path: '/' });
event.cookies.delete(REFRESH_TOKEN_COOKIE, { path: '/' });
event.cookies.delete('refresh_user_id', { path: '/' });
clearSessionCookies(event.cookies);
}
}