Store Google avatar bytes and slim the session JWT to id only.
Download profile images on login into picturebinary, return them as base64 from /auth/me, and resolve sessions via getById.
This commit is contained in:
@@ -16,17 +16,17 @@ export function clearTokenCookie(res) {
|
||||
res.clearCookie('token', TOKEN_COOKIE);
|
||||
}
|
||||
|
||||
export function getCachedUser(email) {
|
||||
const cached = userCache.get(email);
|
||||
export function getCachedUser(authId) {
|
||||
const cached = userCache.get(authId);
|
||||
if (cached && cached.expiresAt > Date.now()) {
|
||||
return cached.user;
|
||||
}
|
||||
userCache.delete(email);
|
||||
userCache.delete(authId);
|
||||
return null;
|
||||
}
|
||||
|
||||
export function setCachedUser(email, user) {
|
||||
userCache.set(email, { user, expiresAt: Date.now() + USER_CACHE_TTL });
|
||||
export function setCachedUser(authId, user) {
|
||||
userCache.set(authId, { user, expiresAt: Date.now() + USER_CACHE_TTL });
|
||||
if (userCache.size > 1000) {
|
||||
const now = Date.now();
|
||||
for (const [k, v] of userCache) {
|
||||
@@ -35,8 +35,8 @@ export function setCachedUser(email, user) {
|
||||
}
|
||||
}
|
||||
|
||||
export function clearCachedUser(email) {
|
||||
userCache.delete(email);
|
||||
export function clearCachedUser(authId) {
|
||||
userCache.delete(authId);
|
||||
}
|
||||
|
||||
function authMeJson(res, req, status, body) {
|
||||
@@ -60,14 +60,24 @@ export const authMiddleware = async (req, res, next) => {
|
||||
|
||||
try {
|
||||
const decoded = jwt.verify(token, env.jwtSecret);
|
||||
const authId = Number(decoded.id);
|
||||
if (!Number.isInteger(authId) || authId <= 0) {
|
||||
clearTokenCookie(res);
|
||||
return authMeJson(res, req, 401, { error: 'Invalid token' });
|
||||
}
|
||||
|
||||
let user = getCachedUser(decoded.email);
|
||||
let user = getCachedUser(authId);
|
||||
if (!user) {
|
||||
user = await db.getAllowedEmail(decoded.email);
|
||||
if (user) setCachedUser(decoded.email, user);
|
||||
const row = await db.getById(authId);
|
||||
if (row) {
|
||||
// Do not cache pictureBinary — large and only needed for /auth/me.
|
||||
const { pictureBinary: _pictureBinary, ...rest } = row;
|
||||
user = rest;
|
||||
setCachedUser(authId, user);
|
||||
}
|
||||
}
|
||||
if (!user) {
|
||||
if (decoded.email) clearCachedUser(decoded.email);
|
||||
clearCachedUser(authId);
|
||||
clearTokenCookie(res);
|
||||
return authMeJson(res, req, 403, { error: 'User is no longer authorized' });
|
||||
}
|
||||
@@ -76,13 +86,11 @@ export const authMiddleware = async (req, res, next) => {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
selectedCompanyId: user.selectedCompanyId,
|
||||
...(user.picture && { picture: user.picture }),
|
||||
};
|
||||
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
if (decoded.exp - now < 2 * 60 * 60) {
|
||||
const { iat, exp, ...payload } = decoded;
|
||||
const newToken = jwt.sign(payload, env.jwtSecret, { expiresIn: '1d' });
|
||||
const newToken = jwt.sign({ id: user.id }, env.jwtSecret, { expiresIn: '1d' });
|
||||
res.cookie('token', newToken, {
|
||||
...TOKEN_COOKIE,
|
||||
maxAge: 24 * 60 * 60 * 1000,
|
||||
|
||||
Reference in New Issue
Block a user