fix(#414): update session config to 7d absolute, 2h idle timeout
All checks were successful
ci/woodpecker/push/api Pipeline was successful

- expiresIn: 7 days (was 24 hours)
- updateAge: 2 hours idle timeout with sliding window
- Explicit cookie attributes: httpOnly, secure in production, sameSite=lax
- Existing sessions expire naturally under old rules

Refs #414

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jason Woltje
2026-02-16 11:24:15 -06:00
parent 447141f05d
commit b316e98b64
2 changed files with 159 additions and 9 deletions

View File

@@ -18,7 +18,12 @@ vi.mock("better-auth/adapters/prisma", () => ({
prismaAdapter: (...args: unknown[]) => mockPrismaAdapter(...args),
}));
import { isOidcEnabled, validateOidcConfig, createAuth } from "./auth.config";
import {
isOidcEnabled,
validateOidcConfig,
createAuth,
getTrustedOrigins,
} from "./auth.config";
describe("auth.config", () => {
// Store original env vars to restore after each test
@@ -32,6 +37,9 @@ describe("auth.config", () => {
delete process.env.OIDC_CLIENT_SECRET;
delete process.env.OIDC_REDIRECT_URI;
delete process.env.NODE_ENV;
delete process.env.NEXT_PUBLIC_APP_URL;
delete process.env.NEXT_PUBLIC_API_URL;
delete process.env.TRUSTED_ORIGINS;
});
afterEach(() => {
@@ -281,4 +289,104 @@ describe("auth.config", () => {
expect(mockGenericOAuth).not.toHaveBeenCalled();
});
});
describe("createAuth - session and cookie configuration", () => {
beforeEach(() => {
mockGenericOAuth.mockClear();
mockBetterAuth.mockClear();
mockPrismaAdapter.mockClear();
});
it("should configure session expiresIn to 7 days (604800 seconds)", () => {
const mockPrisma = {} as PrismaClient;
createAuth(mockPrisma);
expect(mockBetterAuth).toHaveBeenCalledOnce();
const config = mockBetterAuth.mock.calls[0][0] as {
session: { expiresIn: number; updateAge: number };
};
expect(config.session.expiresIn).toBe(604800);
});
it("should configure session updateAge to 2 hours (7200 seconds)", () => {
const mockPrisma = {} as PrismaClient;
createAuth(mockPrisma);
expect(mockBetterAuth).toHaveBeenCalledOnce();
const config = mockBetterAuth.mock.calls[0][0] as {
session: { expiresIn: number; updateAge: number };
};
expect(config.session.updateAge).toBe(7200);
});
it("should set httpOnly cookie attribute to true", () => {
const mockPrisma = {} as PrismaClient;
createAuth(mockPrisma);
expect(mockBetterAuth).toHaveBeenCalledOnce();
const config = mockBetterAuth.mock.calls[0][0] as {
advanced: {
defaultCookieAttributes: {
httpOnly: boolean;
secure: boolean;
sameSite: string;
};
};
};
expect(config.advanced.defaultCookieAttributes.httpOnly).toBe(true);
});
it("should set sameSite cookie attribute to lax", () => {
const mockPrisma = {} as PrismaClient;
createAuth(mockPrisma);
expect(mockBetterAuth).toHaveBeenCalledOnce();
const config = mockBetterAuth.mock.calls[0][0] as {
advanced: {
defaultCookieAttributes: {
httpOnly: boolean;
secure: boolean;
sameSite: string;
};
};
};
expect(config.advanced.defaultCookieAttributes.sameSite).toBe("lax");
});
it("should set secure cookie attribute to true in production", () => {
process.env.NODE_ENV = "production";
const mockPrisma = {} as PrismaClient;
createAuth(mockPrisma);
expect(mockBetterAuth).toHaveBeenCalledOnce();
const config = mockBetterAuth.mock.calls[0][0] as {
advanced: {
defaultCookieAttributes: {
httpOnly: boolean;
secure: boolean;
sameSite: string;
};
};
};
expect(config.advanced.defaultCookieAttributes.secure).toBe(true);
});
it("should set secure cookie attribute to false in non-production", () => {
process.env.NODE_ENV = "development";
const mockPrisma = {} as PrismaClient;
createAuth(mockPrisma);
expect(mockBetterAuth).toHaveBeenCalledOnce();
const config = mockBetterAuth.mock.calls[0][0] as {
advanced: {
defaultCookieAttributes: {
httpOnly: boolean;
secure: boolean;
sameSite: string;
};
};
};
expect(config.advanced.defaultCookieAttributes.secure).toBe(false);
});
});
});