При работе с библиотеками уровня session encryption (в том числе Iron-based решениями) ключевым элементом является контроль времени жизни токена. Любой защищённый токен содержит метаданные, включающие timestamp создания и параметры TTL (time to live), которые используются при дешифрации для определения его актуальности.
При каждом запросе сервер выполняет декодирование зашифрованного значения и извлекает внутреннюю структуру:
Далее выполняется сравнение текущего времени сервера с сохранёнными параметрами.
const isTokenExpired = (session) => {
const now = Date.now();
return now > session.expiresAt;
};
Если токен признан истёкшим, дальнейшая обработка сессии прекращается до этапа бизнес-логики.
После определения недействительности токена сервер обязан привести состояние к безопасному:
В типичной реализации middleware это выглядит как ранний выход из цепочки обработки:
export const sessionMiddleware = (options) => {
return async (req, res, next) => {
const session = await decryptSession(req.cookies.session);
if (!session || isTokenExpired(session)) {
res.setHeader('Set-Cookie', 'session=; Max-Age=0; Path=/; HttpOnly');
return res.status(401).json({ error: 'Session expired' });
}
req.session = session;
next();
};
};
Существует несколько подходов к обработке просроченных токенов, выбор которых зависит от архитектуры приложения.
Самый строгий вариант. При истечении срока действия пользователь полностью разлогинивается.
Используется в системах с повышенными требованиями безопасности, например:
Недостаток — ухудшение пользовательского опыта.
Более гибкая модель, при которой access token имеет короткое время жизни, а refresh token — длительное.
Алгоритм:
if (isAccessTokenExpired(token)) {
const newToken = await refreshAccessToken(refreshToken);
if (!newToken) {
return res.status(401).send('Re-authentication required');
}
res.setHeader('Authorization', `Bearer ${newToken}`);
}
Часто применяется sliding expiration — продление срока жизни токена при активности пользователя.
Каждый валидный запрос обновляет expiresAt:
const extendSession = (session) => {
session.expiresAt = Date.now() + session.maxAge;
return session;
};
Такой подход снижает вероятность внезапного выхода пользователя из системы при активной работе.
Проблема возникает при различии времени клиента и сервера. Это приводит к ложным срабатываниям истечения токена.
Для компенсации вводится допустимый буфер:
const CLOCK_SKEW = 60 * 1000; // 1 минута
const isTokenExpired = (session) => {
return Date.now() - CLOCK_SKEW > session.expiresAt;
};
Это особенно важно в распределённых системах.
При истечении токена важно предотвращать его повторное использование:
Пример проверки версии:
if (session.version !== user.currentSessionVersion) {
throw new Error('Session invalidated');
}
Клиентская часть должна корректно реагировать на истёкший токен:
axios.interceptors.response.use(
response => response,
async (error) => {
if (error.response.status === 401) {
const refreshed = await tryRefreshToken();
if (!refreshed) {
window.location.href = '/login';
}
}
}
);
Системы продакшн-уровня фиксируют события истечения токенов для анализа:
Это позволяет выявлять:
При высокой нагрузке важно минимизировать стоимость операций:
В системах на основе Iron encryption проверка обычно происходит локально, без внешних запросов, что снижает задержки.
Истёкший токен может сопровождаться повреждением структуры (обрезанные cookies, некорректное декодирование). В таких случаях применяется единый fallback:
try {
const session = decrypt(token);
if (isTokenExpired(session)) throw new Error();
} catch {
return null;
}