feat(auth): add 'remember me' functionality and token refresh logic

This commit is contained in:
Daniel Volz
2025-12-27 21:59:21 +01:00
parent 65f007732a
commit cfb8494be3
8 changed files with 131 additions and 14 deletions
+2 -2
View File
@@ -40,8 +40,8 @@ const app = Fastify({
const origins = env.CORS_ORIGINS.split(",").map((o) => o.trim()).filter(Boolean);
// Auth token TTLs (hardcoded - no need for user configuration)
const accessTtlMinutes = 15; // Access token: 15 minutes
const refreshTtlDays = 14; // Refresh token: 14 days
const accessTtlMinutes = env.ACCESS_TOKEN_TTL_MINUTES; // Access token TTL
const refreshTtlDays = env.REFRESH_TOKEN_TTL_DAYS; // Refresh token TTL
const baseCookieOptions: CookieSerializeOptions = {
httpOnly: true,
+4
View File
@@ -23,6 +23,10 @@ const EnvSchema = z.object({
JWT_SECRET: z.string().min(10).optional(),
REFRESH_SECRET: z.string().min(10).optional(),
COOKIE_SECRET: z.string().min(10).optional(),
// Token TTL settings
ACCESS_TOKEN_TTL_MINUTES: z.string().transform((v) => parseInt(v, 10)).default("15"),
REFRESH_TOKEN_TTL_DAYS: z.string().transform((v) => parseInt(v, 10)).default("7"),
});
export type Env = z.infer<typeof EnvSchema>;
+13 -4
View File
@@ -36,6 +36,7 @@ const registerSchema = z.object({
const loginSchema = z.object({
username: z.string().min(1, "Username is required"),
password: z.string().min(1, "Password is required"),
rememberMe: z.boolean().optional().default(false),
});
const updateProfileSchema = z.object({
@@ -141,7 +142,7 @@ export async function authRoutes(app: FastifyInstance) {
});
}
const { username, password } = parsed.data;
const { username, password, rememberMe } = parsed.data;
// Find user by username
const [user] = await db.select().from(users).where(eq(users.username, username));
@@ -196,11 +197,19 @@ export async function authRoutes(app: FastifyInstance) {
{ expiresIn: `${refreshTtlDays}d`, key: app.config.refreshSecret }
);
app.log.info(`User logged in: ${username}`);
app.log.info(`User logged in: ${username} (rememberMe: ${rememberMe})`);
// Cookie options: with maxAge for "remember me", without for session cookie
const accessCookieOptions = rememberMe
? app.config.cookieOptions
: { ...app.config.cookieOptions, maxAge: undefined };
const refreshCookieOptions = rememberMe
? app.config.refreshCookieOptions
: { ...app.config.refreshCookieOptions, maxAge: undefined };
return reply
.setCookie("access_token", accessToken, app.config.cookieOptions)
.setCookie("refresh_token", refreshToken, app.config.refreshCookieOptions)
.setCookie("access_token", accessToken, accessCookieOptions)
.setCookie("refresh_token", refreshToken, refreshCookieOptions)
.send({
ok: true,
user: {