В процессе разработки и тестирования систем аутентификации важно проверять поведение приложения при работе с недействительными токенами. Один из ключевых сценариев — обработка токенов с истёкшим сроком действия. Это позволяет убедиться, что:
Библиотека jose в JavaScript предоставляет гибкие инструменты для управления временем жизни токенов и позволяет намеренно создавать уже истёкшие токены.
JWT (JSON Web Token) содержит стандартные поля (claims), связанные со временем:
Ключевым для тестирования является именно поле exp.
Пример структуры payload:
{
"sub": "user123",
"exp": 1710000000,
"iat": 1709990000
}
В библиотеке jose используется класс
SignJWT. Для создания истёкшего токена необходимо задать
время exp в прошлом.
import { SignJWT } from 'jose'
const secret = new TextEncoder().encode('super-secret-key')
const expiredToken = await new SignJWT({ userId: '123' })
.setProtectedHeader({ alg: 'HS256' })
.setIssuedAt()
.setExpirationTime('-10s')
.sign(secret)
console.log(expiredToken)
Метод .setExpirationTime() принимает:
'10s', '2h',
'1d')В данном случае '−10s' означает, что токен истёк 10
секунд назад.
Иногда требуется полный контроль над временем. В этом случае можно
вручную задать exp.
const now = Math.floor(Date.now() / 1000)
const expiredToken = await new SignJWT({ userId: '123' })
.setProtectedHeader({ alg: 'HS256' })
.setIssuedAt(now - 100)
.setExpirationTime(now - 50)
.sign(secret)
Здесь:
Для верификации используется функция jwtVerify.
import { jwtVerify } from 'jose'
try {
await jwtVerify(expiredToken, secret)
} catch (err) {
console.error(err)
}
При проверке истёкшего токена библиотека выбрасывает ошибку:
JWTExpired: "exp" claim timestamp check failed
В реальных системах возможны рассинхронизации времени между
сервисами. Для тестирования таких сценариев используется параметр
clockTolerance.
await jwtVerify(expiredToken, secret, {
clockTolerance: 60 // секунд
})
Если токен истёк менее чем на 60 секунд назад, он будет считаться валидным.
Это полезно для:
Для некоторых тестов требуется принудительно игнорировать истечение срока.
await jwtVerify(expiredToken, secret, {
ignoreExp: true
})
Это позволяет:
Для полного контроля над временем проверки можно задать “виртуальное текущее время”.
await jwtVerify(expiredToken, secret, {
clockTimestamp: Math.floor(Date.now() / 1000) - 1000
})
Это позволяет:
Создание истёкшего токена и отправка его в API:
fetch('/api/protected', {
headers: {
Authorization: `Bearer ${expiredToken}`
}
})
Ожидаемое поведение:
Последовательность:
Истёкшие токены не должны приниматься даже при повторной отправке:
Пример с использованием Jest:
test('expired token should throw error', async () => {
await expect(jwtVerify(expiredToken, secret))
.rejects
.toThrow('exp')
})
.setExpirationTime(Date.now())
Ошибка: используется миллисекунды вместо секунд.
Правильно:
.setExpirationTime(Math.floor(Date.now() / 1000))
iatНекоторые системы требуют iat для корректной логики:
.setIssuedAt()
Если токен истёк слишком давно, clockTolerance не
поможет.
nbf и iatПрактика показывает эффективность создания набора токенов с разными состояниями:
const tokens = {
valid: await new SignJWT({}).setExpirationTime('1h').sign(secret),
expired: await new SignJWT({}).setExpirationTime('-1h').sign(secret),
almostExpired: await new SignJWT({}).setExpirationTime('1s').sign(secret),
}
Это упрощает покрытие тестами и повышает читаемость.
При работе с истёкшими токенами важно логировать:
expПример:
const payload = JSON.parse(Buffer.from(token.split('.')[1], 'base64'))
console.log({
now: Math.floor(Date.now() / 1000),
exp: payload.exp,
diff: payload.exp - Math.floor(Date.now() / 1000)
})
Для нагрузочного тестирования:
const tokens = await Promise.all(
Array.from({ length: 1000 }, () =>
new SignJWT({ user: 'test' })
.setExpirationTime('-1m')
.sign(secret)
)
)
new SignJWT({
role: 'admin',
permissions: ['read', 'write']
})
.setExpirationTime('-30s')
Позволяет тестировать сложные сценарии авторизации.
.setProtectedHeader({ alg: 'RS256' })
Проверка поведения системы при разных способах подписи.
Глубокое понимание механизмов истечения токенов и возможностей библиотеки jose позволяет точно моделировать критические сценарии, выявлять ошибки в логике аутентификации и повышать надёжность системы.