Fix cookie Secure flag for HTTP dev, un-track generated nginx confs
- Cookie Secure flag now uses req.secure (respects trust proxy + X-Forwarded-Proto) instead of NODE_ENV. Works correctly over both HTTP (local dev) and HTTPS (production tunnel). - SameSite=Strict over HTTPS, SameSite=Lax over HTTP (browsers reject Strict cookies over plain HTTP). - Un-track generated nginx/conf.d/api.conf and services.conf (gitignored, regenerated from templates at startup). - Update CLAUDE.md: ENCRYPTION_KEY now required in all environments. Bunker Admin
This commit is contained in:
@@ -22,23 +22,25 @@ const router = Router();
|
||||
const REFRESH_COOKIE_NAME = 'cml_refresh';
|
||||
const REFRESH_COOKIE_MAX_AGE = 7 * 24 * 60 * 60 * 1000; // 7 days in ms
|
||||
|
||||
/** Set the refresh token as an httpOnly cookie */
|
||||
function setRefreshCookie(res: Response, token: string) {
|
||||
/** Set the refresh token as an httpOnly cookie.
|
||||
* Uses req.secure (respects trust proxy + X-Forwarded-Proto) to determine
|
||||
* the Secure flag, so it works correctly over both HTTP (dev) and HTTPS (tunnel). */
|
||||
function setRefreshCookie(req: Request, res: Response, token: string) {
|
||||
res.cookie(REFRESH_COOKIE_NAME, token, {
|
||||
httpOnly: true,
|
||||
secure: env.NODE_ENV === 'production',
|
||||
sameSite: 'strict',
|
||||
secure: req.secure,
|
||||
sameSite: req.secure ? 'strict' : 'lax',
|
||||
maxAge: REFRESH_COOKIE_MAX_AGE,
|
||||
path: '/api/auth',
|
||||
});
|
||||
}
|
||||
|
||||
/** Clear the refresh token cookie */
|
||||
function clearRefreshCookie(res: Response) {
|
||||
function clearRefreshCookie(req: Request, res: Response) {
|
||||
res.clearCookie(REFRESH_COOKIE_NAME, {
|
||||
httpOnly: true,
|
||||
secure: env.NODE_ENV === 'production',
|
||||
sameSite: 'strict',
|
||||
secure: req.secure,
|
||||
sameSite: req.secure ? 'strict' : 'lax',
|
||||
path: '/api/auth',
|
||||
});
|
||||
}
|
||||
@@ -52,7 +54,7 @@ router.post(
|
||||
try {
|
||||
const result = await authService.login(req.body.email, req.body.password);
|
||||
// Set refresh token as httpOnly cookie (not in response body)
|
||||
setRefreshCookie(res, result.refreshToken);
|
||||
setRefreshCookie(req, res, result.refreshToken);
|
||||
const { refreshToken: _, ...responseWithoutRefresh } = result;
|
||||
res.json(responseWithoutRefresh);
|
||||
} catch (err) {
|
||||
@@ -71,7 +73,7 @@ router.post(
|
||||
const result = await authService.register(req.body);
|
||||
// Set refresh token as httpOnly cookie if tokens were issued (non-verification path)
|
||||
if ('refreshToken' in result && result.refreshToken) {
|
||||
setRefreshCookie(res, result.refreshToken);
|
||||
setRefreshCookie(req, res, result.refreshToken);
|
||||
const { refreshToken: _, ...responseWithoutRefresh } = result;
|
||||
res.status(201).json(responseWithoutRefresh);
|
||||
} else {
|
||||
@@ -278,11 +280,11 @@ router.post(
|
||||
}
|
||||
const result = await authService.refreshTokens(refreshToken);
|
||||
// Set new refresh token as httpOnly cookie
|
||||
setRefreshCookie(res, result.refreshToken);
|
||||
setRefreshCookie(req, res, result.refreshToken);
|
||||
const { refreshToken: _, ...responseWithoutRefresh } = result;
|
||||
res.json(responseWithoutRefresh);
|
||||
} catch (err) {
|
||||
clearRefreshCookie(res);
|
||||
clearRefreshCookie(req, res);
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
@@ -299,10 +301,10 @@ router.post(
|
||||
if (refreshToken) {
|
||||
await authService.logout(refreshToken);
|
||||
}
|
||||
clearRefreshCookie(res);
|
||||
clearRefreshCookie(req, res);
|
||||
res.json({ message: 'Logged out' });
|
||||
} catch (err) {
|
||||
clearRefreshCookie(res);
|
||||
clearRefreshCookie(req, res);
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,11 +34,11 @@ router.post(
|
||||
async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const result = await volunteerInviteService.redeemInvite(req.body);
|
||||
// Set refresh token as httpOnly cookie
|
||||
// Set refresh token as httpOnly cookie (Secure flag based on actual protocol)
|
||||
res.cookie('cml_refresh', result.tokens.refreshToken, {
|
||||
httpOnly: true,
|
||||
secure: env.NODE_ENV === 'production',
|
||||
sameSite: 'strict',
|
||||
secure: req.secure,
|
||||
sameSite: req.secure ? 'strict' : 'lax',
|
||||
maxAge: 7 * 24 * 60 * 60 * 1000,
|
||||
path: '/api/auth',
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user