Procházet zdrojové kódy

Refresh auth cookie automatically

flyzto před 1 měsícem
rodič
revize
44408297b4
3 změnil soubory, kde provedl 33 přidání a 8 odebrání
  1. 29 7
      server/libs/auth.js
  2. 2 1
      server/middleware/requireAuth.js
  3. 2 0
      server/routes/auth.js

+ 29 - 7
server/libs/auth.js

@@ -2,13 +2,19 @@ import crypto from 'node:crypto';
 
 const COOKIE_NAME = 'ppai_session';
 const DEFAULT_MAX_AGE = 12;
+const DEFAULT_REFRESH_THRESHOLD = 2;
 
-const getConfig = () => ({
-  username: process.env.PPAI_AUTH_USER || 'admin',
-  password: process.env.PPAI_AUTH_PASSWORD || 'admin123',
-  secret: process.env.PPAI_AUTH_SECRET || 'ppai-dev-secret',
-  maxAge: Number(process.env.PPAI_AUTH_MAX_AGE || DEFAULT_MAX_AGE) * 60 * 60 * 1000,
-});
+const getConfig = () => {
+  const maxAge = Number(process.env.PPAI_AUTH_MAX_AGE || DEFAULT_MAX_AGE) * 60 * 60 * 1000;
+
+  return {
+    username: process.env.PPAI_AUTH_USER || 'admin',
+    password: process.env.PPAI_AUTH_PASSWORD || 'admin123',
+    secret: process.env.PPAI_AUTH_SECRET || 'ppai-dev-secret',
+    maxAge,
+    refreshThreshold: Number(process.env.PPAI_AUTH_REFRESH_THRESHOLD || DEFAULT_REFRESH_THRESHOLD),
+  };
+};
 
 const base64UrlEncode = (value) => Buffer.from(value).toString('base64url');
 const base64UrlDecode = (value) => Buffer.from(value, 'base64url').toString();
@@ -56,6 +62,22 @@ export const createSession = (username) => {
   return `${payload}.${signature}`;
 };
 
+export const refreshSessionIfNeeded = (res, session) => {
+  if (!session?.username || !session?.exp) {
+    return false;
+  }
+
+  const { refreshThreshold } = getConfig();
+  const remainingTime = session.exp - Date.now();
+
+  if (remainingTime > refreshThreshold) {
+    return false;
+  }
+
+  res.cookie(authCookieName, createSession(session.username), cookieOptions());
+  return true;
+};
+
 export const verifySession = (token) => {
   if (!token || typeof token !== 'string') {
     return null;
@@ -77,7 +99,7 @@ export const verifySession = (token) => {
     if (!session?.username || !session?.exp || Date.now() > session.exp) {
       return null;
     }
-    return { username: session.username };
+    return { username: session.username, exp: session.exp };
   }
   catch {
     return null;

+ 2 - 1
server/middleware/requireAuth.js

@@ -1,4 +1,4 @@
-import { authCookieName, verifySession } from '../libs/auth.js';
+import { authCookieName, refreshSessionIfNeeded, verifySession } from '../libs/auth.js';
 
 const requireAuth = (req, res, next) => {
   const session = verifySession(req.cookies?.[authCookieName]);
@@ -8,6 +8,7 @@ const requireAuth = (req, res, next) => {
   }
 
   req.user = session;
+  refreshSessionIfNeeded(res, session);
   next();
 };
 

+ 2 - 0
server/routes/auth.js

@@ -4,6 +4,7 @@ import {
   clearCookieOptions,
   cookieOptions,
   createSession,
+  refreshSessionIfNeeded,
   validateCredentials,
   verifySession,
 } from '../libs/auth.js';
@@ -34,6 +35,7 @@ router.get('/me', (req, res) => {
     return res.unauthorized('请先登录');
   }
 
+  refreshSessionIfNeeded(res, session);
   return res.sendSuccess({ username: session.username });
 });