feat: cookie Secure conditionnel + sous-commande d'installation de service
Cookie: routes/auth.ts pose `secure` sur le cookie de session quand la requête arrive en HTTPS (x-forwarded-proto), sans trustProxy — durcit le cookie derrière Tailscale Serve sans casser le localhost http. Install: nouveau cli/install.ts + routeur de sous-commandes dans index.ts (install/uninstall/status/serve). Service utilisateur systemd (Linux) ou launchd (macOS), bootstrap du token, --dry-run/--no-enable. Rétrocompat stricte du daemon par défaut (runDaemon extrait). Tests: app.e2e (cookie Secure local vs HTTPS) + cli-install (fonctions pures). 203/203 verts, acceptation P1/P4 vertes. Docs: README.md + README.fr.md (installeur multi-OS, distinction utiliser/cloner, modèle de sécurité durci). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,16 @@
|
||||
import type { FastifyInstance } from 'fastify';
|
||||
import type { FastifyInstance, FastifyRequest } from 'fastify';
|
||||
import type { LoginRequest, LoginResponse, MeResponse } from '@arboretum/shared';
|
||||
import type { AuthService, LoginRateLimiter } from '../auth/service.js';
|
||||
|
||||
// Tailscale Serve / un reverse-proxy TLS posent x-forwarded-proto. On ne sert jamais
|
||||
// en TLS direct : `secure` n'est posé que derrière un front HTTPS, jamais en localhost http
|
||||
// (sinon le navigateur refuserait le cookie sur http://127.0.0.1 et le login local casserait).
|
||||
function isHttpsRequest(req: FastifyRequest): boolean {
|
||||
const xfp = req.headers['x-forwarded-proto'];
|
||||
const proto = (Array.isArray(xfp) ? xfp[0] : xfp)?.split(',')[0]?.trim();
|
||||
return proto === 'https';
|
||||
}
|
||||
|
||||
export function registerAuthRoutes(
|
||||
app: FastifyInstance,
|
||||
auth: AuthService,
|
||||
@@ -24,6 +33,7 @@ export function registerAuthRoutes(
|
||||
path: '/',
|
||||
httpOnly: true,
|
||||
sameSite: 'strict',
|
||||
secure: isHttpsRequest(req),
|
||||
maxAge: 30 * 24 * 3600,
|
||||
});
|
||||
const res: LoginResponse = { ok: true, label: ctx.label };
|
||||
@@ -35,8 +45,9 @@ export function registerAuthRoutes(
|
||||
return reply.send(res);
|
||||
});
|
||||
|
||||
app.post('/api/v1/auth/logout', async (_req, reply) => {
|
||||
void reply.clearCookie(auth.cookieName, { path: '/' });
|
||||
app.post('/api/v1/auth/logout', async (req, reply) => {
|
||||
// Les attributs doivent matcher ceux posés au login pour que le navigateur efface bien le cookie.
|
||||
void reply.clearCookie(auth.cookieName, { path: '/', secure: isHttpsRequest(req) });
|
||||
return reply.send({ ok: true });
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user