feat: увеличить время жизни access token до 24 часов, сделать refresh token бессрочным
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 33s
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 33s
This commit is contained in:
@@ -301,7 +301,7 @@ func (a *App) generateAccessToken(userID int) (string, error) {
|
|||||||
claims := JWTClaims{
|
claims := JWTClaims{
|
||||||
UserID: userID,
|
UserID: userID,
|
||||||
RegisteredClaims: jwt.RegisteredClaims{
|
RegisteredClaims: jwt.RegisteredClaims{
|
||||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(15 * time.Minute)),
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
|
||||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -460,7 +460,7 @@ func (a *App) registerHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
_, err = a.DB.Exec(`
|
_, err = a.DB.Exec(`
|
||||||
INSERT INTO refresh_tokens (user_id, token_hash, expires_at)
|
INSERT INTO refresh_tokens (user_id, token_hash, expires_at)
|
||||||
VALUES ($1, $2, $3)
|
VALUES ($1, $2, $3)
|
||||||
`, user.ID, refreshTokenHash, time.Now().Add(7*24*time.Hour))
|
`, user.ID, refreshTokenHash, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error storing refresh token: %v", err)
|
log.Printf("Error storing refresh token: %v", err)
|
||||||
}
|
}
|
||||||
@@ -472,7 +472,7 @@ func (a *App) registerHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
json.NewEncoder(w).Encode(TokenResponse{
|
json.NewEncoder(w).Encode(TokenResponse{
|
||||||
AccessToken: accessToken,
|
AccessToken: accessToken,
|
||||||
RefreshToken: refreshToken,
|
RefreshToken: refreshToken,
|
||||||
ExpiresIn: 900, // 15 minutes
|
ExpiresIn: 86400, // 24 hours
|
||||||
User: user,
|
User: user,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -567,7 +567,7 @@ func (a *App) loginHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
_, err = a.DB.Exec(`
|
_, err = a.DB.Exec(`
|
||||||
INSERT INTO refresh_tokens (user_id, token_hash, expires_at)
|
INSERT INTO refresh_tokens (user_id, token_hash, expires_at)
|
||||||
VALUES ($1, $2, $3)
|
VALUES ($1, $2, $3)
|
||||||
`, user.ID, refreshTokenHash, time.Now().Add(7*24*time.Hour))
|
`, user.ID, refreshTokenHash, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error storing refresh token: %v", err)
|
log.Printf("Error storing refresh token: %v", err)
|
||||||
}
|
}
|
||||||
@@ -579,7 +579,7 @@ func (a *App) loginHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
json.NewEncoder(w).Encode(TokenResponse{
|
json.NewEncoder(w).Encode(TokenResponse{
|
||||||
AccessToken: accessToken,
|
AccessToken: accessToken,
|
||||||
RefreshToken: refreshToken,
|
RefreshToken: refreshToken,
|
||||||
ExpiresIn: 900,
|
ExpiresIn: 86400, // 24 hours
|
||||||
User: user,
|
User: user,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -603,12 +603,12 @@ func (a *App) refreshTokenHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find valid refresh token
|
// Find valid refresh token (expires_at is NULL for tokens without expiration)
|
||||||
rows, err := a.DB.Query(`
|
rows, err := a.DB.Query(`
|
||||||
SELECT rt.id, rt.user_id, rt.token_hash, u.email, u.name, u.created_at, u.updated_at, u.is_active, u.last_login_at
|
SELECT rt.id, rt.user_id, rt.token_hash, u.email, u.name, u.created_at, u.updated_at, u.is_active, u.last_login_at
|
||||||
FROM refresh_tokens rt
|
FROM refresh_tokens rt
|
||||||
JOIN users u ON rt.user_id = u.id
|
JOIN users u ON rt.user_id = u.id
|
||||||
WHERE rt.expires_at > NOW()
|
WHERE rt.expires_at IS NULL OR rt.expires_at > NOW()
|
||||||
`)
|
`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error querying refresh tokens: %v", err)
|
log.Printf("Error querying refresh tokens: %v", err)
|
||||||
@@ -669,13 +669,13 @@ func (a *App) refreshTokenHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
a.DB.Exec(`
|
a.DB.Exec(`
|
||||||
INSERT INTO refresh_tokens (user_id, token_hash, expires_at)
|
INSERT INTO refresh_tokens (user_id, token_hash, expires_at)
|
||||||
VALUES ($1, $2, $3)
|
VALUES ($1, $2, $3)
|
||||||
`, user.ID, refreshTokenHash, time.Now().Add(7*24*time.Hour))
|
`, user.ID, refreshTokenHash, nil)
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
json.NewEncoder(w).Encode(TokenResponse{
|
json.NewEncoder(w).Encode(TokenResponse{
|
||||||
AccessToken: accessToken,
|
AccessToken: accessToken,
|
||||||
RefreshToken: refreshToken,
|
RefreshToken: refreshToken,
|
||||||
ExpiresIn: 900,
|
ExpiresIn: 86400, // 24 hours
|
||||||
User: user,
|
User: user,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -2568,8 +2568,8 @@ func (a *App) initAuthDB() error {
|
|||||||
// Не возвращаем ошибку, чтобы приложение могло запуститься
|
// Не возвращаем ошибку, чтобы приложение могло запуститься
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up expired refresh tokens
|
// Clean up expired refresh tokens (only those with expiration date set)
|
||||||
a.DB.Exec("DELETE FROM refresh_tokens WHERE expires_at < NOW()")
|
a.DB.Exec("DELETE FROM refresh_tokens WHERE expires_at IS NOT NULL AND expires_at < NOW()")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
-- Migration: Make refresh tokens permanent (no expiration)
|
||||||
|
-- Refresh tokens теперь не имеют срока действия (expires_at может быть NULL)
|
||||||
|
-- Access tokens живут 24 часа вместо 15 минут
|
||||||
|
|
||||||
|
-- ============================================
|
||||||
|
-- 1. Изменяем expires_at на NULLABLE
|
||||||
|
-- ============================================
|
||||||
|
ALTER TABLE refresh_tokens
|
||||||
|
ALTER COLUMN expires_at DROP NOT NULL;
|
||||||
|
|
||||||
|
-- ============================================
|
||||||
|
-- 2. Устанавливаем NULL для всех существующих токенов
|
||||||
|
-- (или можно оставить их как есть, если они еще не истекли)
|
||||||
|
-- ============================================
|
||||||
|
-- UPDATE refresh_tokens SET expires_at = NULL WHERE expires_at > NOW();
|
||||||
|
|
||||||
|
-- ============================================
|
||||||
|
-- 3. Комментарий
|
||||||
|
-- ============================================
|
||||||
|
COMMENT ON COLUMN refresh_tokens.expires_at IS 'Expiration date for refresh token. NULL means token never expires.';
|
||||||
|
|
||||||
Reference in New Issue
Block a user