fix(security): JWT validate against prod gateway only + cap grace renewal

- jwt: drop test/dev gateway from nubesAPIEndpoints (test-stand token was accepted as prod)
- cache: grace bounded by graceDeadline from last successful validation (no more indefinite renewal)
This commit is contained in:
Repinoid
2026-10-10 17:07:57 +03:00
parent 521e3d85d0
commit e21abb8f13
2 changed files with 18 additions and 11 deletions
+14 -7
View File
@@ -28,6 +28,7 @@ const cacheCleanupInterval = 5 * time.Minute
type tokenCacheEntry struct {
validUntil time.Time
graceDeadline time.Time // потолок grace от последней УСПЕШНОЙ проверки (не продлевается)
}
var (
@@ -66,18 +67,20 @@ func PingNubesAPICached(ctx context.Context, endpoint, token string) error {
return err
}
if err != nil {
// Сетевая ошибка шлюза: если токен ранее был валиден — даём grace.
if ok {
tokenCacheMu.Lock()
tokenCache[key] = tokenCacheEntry{validUntil: time.Now().Add(cacheGraceTTL)}
tokenCacheMu.Unlock()
// Сетевая ошибка шлюза: если токен ранее был валиден — даём grace, но
// НЕ дальше graceDeadline. Раньше validUntil сдвигали вперёд на каждый
// сетевой сбой — отозванный токен жил бы вечно при повторных ошибках.
if ok && now.Before(entry.graceDeadline) {
return nil
}
return err
}
tokenCacheMu.Lock()
tokenCache[key] = tokenCacheEntry{validUntil: time.Now().Add(cacheTTL)}
tokenCache[key] = tokenCacheEntry{
validUntil: now.Add(cacheTTL),
graceDeadline: now.Add(cacheGraceTTL),
}
tokenCacheMu.Unlock()
return nil
}
@@ -85,8 +88,12 @@ func PingNubesAPICached(ctx context.Context, endpoint, token string) error {
// WarmTokenCache — записывает токен в кэш как валидный без вызова шлюза.
// Используется после успешной свежей проверки при логине (jwtAuth).
func WarmTokenCache(token string) {
now := time.Now()
tokenCacheMu.Lock()
tokenCache[tokenCacheKey(token)] = tokenCacheEntry{validUntil: time.Now().Add(cacheTTL)}
tokenCache[tokenCacheKey(token)] = tokenCacheEntry{
validUntil: now.Add(cacheTTL),
graceDeadline: now.Add(cacheGraceTTL),
}
tokenCacheMu.Unlock()
}
+3 -3
View File
@@ -71,11 +71,11 @@ var ErrTokenRejected = errors.New("token rejected by nubes API")
// nubesAPIEndpoints — стенды Nubes API Gateway, по которым валидируется токен.
// Токен принимается, если ХОТЯ БЫ ОДИН стенд его принял.
// Юзер может вводить токен от любого стенда — главное, что токен действительный.
// Безопасность: только prod-шлюз. Тестовый/dev-шлюзы принимают токены своих
// стендов, и при их наличии токен тестового стенда считался бы валидным на проде.
// Специфичный endpoint можно переопределить через NUBES_ENDPOINT.
var nubesAPIEndpoints = []string{
"https://lk-api-gateway.ngcloud.ru/api/v1/svc",
"https://lk-api-gateway-test.ngcloud.ru/api/v1/svc",
"https://lk-api-gateway-dev.ngcloud.ru/api/v1/svc",
}
// nubesHTTPClient — общий клиент для валидации токенов: переиспользует