3.28.3: Исправлена проблема с refresh token
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 54s
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 54s
This commit is contained in:
@@ -1018,10 +1018,7 @@ func (a *App) refreshTokenHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete old refresh token
|
// Generate new tokens FIRST before deleting old one to prevent race condition
|
||||||
a.DB.Exec("DELETE FROM refresh_tokens WHERE id = $1", foundTokenID)
|
|
||||||
|
|
||||||
// Generate new tokens
|
|
||||||
accessToken, err := a.generateAccessToken(user.ID)
|
accessToken, err := a.generateAccessToken(user.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error generating access token: %v", err)
|
log.Printf("Error generating access token: %v", err)
|
||||||
@@ -1036,12 +1033,21 @@ func (a *App) refreshTokenHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store new refresh token
|
// Store new refresh token FIRST
|
||||||
refreshTokenHash, _ := hashPassword(refreshToken)
|
refreshTokenHash, _ := hashPassword(refreshToken)
|
||||||
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, nil)
|
`, user.ID, refreshTokenHash, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error storing new refresh token: %v", err)
|
||||||
|
sendErrorWithCORS(w, "Error generating token", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete old refresh token AFTER new one is successfully stored
|
||||||
|
// This prevents race condition where multiple refresh requests might use the same token
|
||||||
|
a.DB.Exec("DELETE FROM refresh_tokens WHERE id = $1", foundTokenID)
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
json.NewEncoder(w).Encode(TokenResponse{
|
json.NewEncoder(w).Encode(TokenResponse{
|
||||||
@@ -2818,6 +2824,29 @@ func (a *App) initAuthDB() error {
|
|||||||
a.DB.Exec("CREATE INDEX IF NOT EXISTS idx_refresh_tokens_user_id ON refresh_tokens(user_id)")
|
a.DB.Exec("CREATE INDEX IF NOT EXISTS idx_refresh_tokens_user_id ON refresh_tokens(user_id)")
|
||||||
a.DB.Exec("CREATE INDEX IF NOT EXISTS idx_refresh_tokens_token_hash ON refresh_tokens(token_hash)")
|
a.DB.Exec("CREATE INDEX IF NOT EXISTS idx_refresh_tokens_token_hash ON refresh_tokens(token_hash)")
|
||||||
|
|
||||||
|
// Apply migration 014: Make refresh tokens permanent (expires_at nullable)
|
||||||
|
// This allows refresh tokens to never expire
|
||||||
|
var isNullable string
|
||||||
|
err = a.DB.QueryRow(`
|
||||||
|
SELECT is_nullable
|
||||||
|
FROM information_schema.columns
|
||||||
|
WHERE table_schema = 'public'
|
||||||
|
AND table_name = 'refresh_tokens'
|
||||||
|
AND column_name = 'expires_at'
|
||||||
|
`).Scan(&isNullable)
|
||||||
|
if err == nil && isNullable == "NO" {
|
||||||
|
// Column is NOT NULL, need to make it nullable
|
||||||
|
if _, err := a.DB.Exec("ALTER TABLE refresh_tokens ALTER COLUMN expires_at DROP NOT NULL"); err != nil {
|
||||||
|
log.Printf("Warning: Failed to apply migration 014 (make expires_at nullable): %v", err)
|
||||||
|
} else {
|
||||||
|
log.Printf("Migration 014 applied: refresh_tokens.expires_at is now nullable")
|
||||||
|
}
|
||||||
|
} else if err == nil && isNullable == "YES" {
|
||||||
|
// Migration already applied
|
||||||
|
log.Printf("Migration 014 already applied (expires_at is nullable), skipping")
|
||||||
|
}
|
||||||
|
// If err != nil, column might not exist yet (shouldn't happen, but ignore)
|
||||||
|
|
||||||
// Add user_id column to all tables if not exists
|
// Add user_id column to all tables if not exists
|
||||||
tables := []string{"projects", "entries", "nodes", "dictionaries", "words", "progress", "configs", "telegram_integrations", "weekly_goals", "tasks"}
|
tables := []string{"projects", "entries", "nodes", "dictionaries", "words", "progress", "configs", "telegram_integrations", "weekly_goals", "tasks"}
|
||||||
for _, table := range tables {
|
for _, table := range tables {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "play-life-web",
|
"name": "play-life-web",
|
||||||
"version": "3.28.2",
|
"version": "3.28.3",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
Reference in New Issue
Block a user