Compare commits

...

2 Commits

Author SHA1 Message Date
Admin
87b5ad1460 feat(auth): add debug-login bypass endpoint secured by DEBUG_LOGIN_TOKEN
All checks were successful
CI / Backend (push) Successful in 43s
CI / UI (push) Successful in 1m10s
Release / Check ui (push) Successful in 26s
Release / Test backend (push) Successful in 41s
CI / Backend (pull_request) Successful in 25s
Release / Docker / caddy (push) Successful in 47s
CI / UI (pull_request) Successful in 41s
Release / Docker / ui (push) Successful in 2m32s
Release / Docker / backend (push) Successful in 3m57s
Release / Docker / runner (push) Successful in 4m8s
Release / Gitea Release (push) Successful in 12s
2026-03-31 21:59:58 +05:00
Admin
168cb52ed0 fix(admin): use --color-surface for drawer bg (--color-bg was undefined)
Some checks failed
CI / Backend (push) Successful in 43s
CI / UI (push) Successful in 58s
Release / Check ui (push) Successful in 26s
Release / Test backend (push) Successful in 38s
Release / Docker / caddy (push) Successful in 45s
CI / Backend (pull_request) Successful in 26s
Release / Docker / ui (push) Failing after 10s
CI / UI (pull_request) Successful in 42s
Release / Docker / runner (push) Failing after 19s
Release / Docker / backend (push) Successful in 1m42s
Release / Gitea Release (push) Has been skipped
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 18:53:58 +05:00
3 changed files with 75 additions and 1 deletions

View File

@@ -290,6 +290,7 @@ services:
POCKETBASE_ADMIN_EMAIL: "${POCKETBASE_ADMIN_EMAIL}"
POCKETBASE_ADMIN_PASSWORD: "${POCKETBASE_ADMIN_PASSWORD}"
AUTH_SECRET: "${AUTH_SECRET}"
DEBUG_LOGIN_TOKEN: "${DEBUG_LOGIN_TOKEN}"
PUBLIC_MINIO_PUBLIC_URL: "${MINIO_PUBLIC_ENDPOINT}"
# Valkey
VALKEY_ADDR: "valkey:6379"

View File

@@ -40,7 +40,7 @@
<aside
class="
fixed top-0 left-0 h-full z-50 w-56 shrink-0 border-r border-(--color-border) px-3 py-6 flex flex-col gap-6
bg-(--color-bg) transition-transform duration-200
bg-(--color-surface) transition-transform duration-200
{sidebarOpen ? 'translate-x-0' : '-translate-x-full'}
md:relative md:translate-x-0 md:w-48 md:z-auto md:top-auto md:h-auto
"

View File

@@ -0,0 +1,73 @@
import { redirect, error } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
import { getUserByUsername, createUserSession } from '$lib/server/pocketbase';
import { createAuthToken } from '../../../../hooks.server';
import { env } from '$env/dynamic/private';
import { log } from '$lib/server/logger';
import { randomBytes } from 'node:crypto';
const AUTH_COOKIE = 'libnovel_auth';
const ONE_YEAR = 60 * 60 * 24 * 365;
/**
* GET /api/auth/debug-login?token=<DEBUG_LOGIN_TOKEN>&username=<username>
*
* One-shot debug bypass: verifies a shared secret token, then mints a real
* auth cookie for the given user (defaults to the first admin account) and
* redirects to /.
*
* Requires DEBUG_LOGIN_TOKEN env var to be set. Disabled (404) when the var
* is absent or empty.
*/
export const GET: RequestHandler = async ({ url, cookies, request }) => {
const debugToken = env.DEBUG_LOGIN_TOKEN ?? '';
if (!debugToken) {
error(404, 'Not found');
}
const provided = url.searchParams.get('token') ?? '';
// Constant-time comparison to prevent timing attacks
if (provided.length !== debugToken.length) {
log.warn('api/auth/debug-login', 'bad token attempt');
error(401, 'Invalid token');
}
let diff = 0;
for (let i = 0; i < debugToken.length; i++) {
diff |= provided.charCodeAt(i) ^ debugToken.charCodeAt(i);
}
if (diff !== 0) {
log.warn('api/auth/debug-login', 'bad token attempt');
error(401, 'Invalid token');
}
const username = url.searchParams.get('username') ?? 'kamil_alekber_2e99';
const user = await getUserByUsername(username);
if (!user) {
error(404, `User '${username}' not found`);
}
const authSessionId = randomBytes(16).toString('hex');
const userAgent = request.headers.get('user-agent') ?? '';
const ip =
request.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ??
request.headers.get('x-real-ip') ??
'debug';
createUserSession(user.id, authSessionId, userAgent, ip).catch((e) =>
log.warn('api/auth/debug-login', 'createUserSession failed (non-fatal)', { err: String(e) })
);
const token = createAuthToken(user.id, user.username, user.role ?? 'user', authSessionId);
cookies.set(AUTH_COOKIE, token, {
path: '/',
httpOnly: true,
sameSite: 'lax',
maxAge: ONE_YEAR
});
log.info('api/auth/debug-login', 'debug login used', { username: user.username, ip });
const next = url.searchParams.get('next') ?? '/';
redirect(302, next);
};