Контекст: Два параллельных POST /auth/refresh с одним refresh token вызывают ложное срабатывание детекции кражи.
Без блокировки потоков:
GetValid → foundGetValid → found (на том же токене, ещё не удалён)Delete → okCreate → новый токен T2GetRevoked → found (т.к. A уже удалил)RevokeAllForUser → все сессии пользователя сброшеныdeleted, err := s.refreshTokenRepo.DeleteIfExists(ctx, storedToken.TokenHash)
if !deleted {
// Токен уже был сротирован — значит, это конкурентный запрос, не кража
return nil, "", ErrTokenReused
}
sequenceDiagram
participant A as Request A
participant B as Request B
participant DB as PostgreSQL
A->>DB: GetValid → found
B->>DB: GetValid → found
A->>DB: DeleteIfExists → true (deleted)
A->>DB: Create (new token)
B->>DB: DeleteIfExists → false (already deleted)
Note over B: ErrTokenReused (graceful, без RevokeAll)
Файлы: backend/internal/services/auth.go:236-243, backend/internal/repository/refresh_tokens.go:143-155
#golang #concurrency #auth #race-condition #security