9153a20384
Fase 3 del issue 0010 — sesiones SQLite como alternativa a JWT. - Tabla sessions creada con CREATE TABLE IF NOT EXISTS (autosetup) - Tokens de 32 bytes aleatorios (crypto/rand) codificados en hex (256 bits) - Indices en user_id y expires_at - Prepared statements para evitar SQL injection - SessionCleanup para eliminar expiradas periodicamente
59 lines
1.4 KiB
Go
59 lines
1.4 KiB
Go
package infra
|
|
|
|
import (
|
|
"database/sql"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
)
|
|
|
|
// SessionValidate busca el token en la tabla sessions, verifica que no este
|
|
// expirado y retorna la Session con su metadata deserializada.
|
|
// Error si el token no existe, esta expirado o la BD falla.
|
|
func SessionValidate(db *sql.DB, token string) (Session, error) {
|
|
var zero Session
|
|
if db == nil {
|
|
return zero, fmt.Errorf("session_validate: db nil")
|
|
}
|
|
if token == "" {
|
|
return zero, fmt.Errorf("session_validate: token vacio")
|
|
}
|
|
|
|
var (
|
|
userID string
|
|
expiresAt int64
|
|
createdAt int64
|
|
metaStr string
|
|
)
|
|
row := db.QueryRow(
|
|
"SELECT user_id, expires_at, created_at, metadata FROM sessions WHERE token = ?",
|
|
token,
|
|
)
|
|
if err := row.Scan(&userID, &expiresAt, &createdAt, &metaStr); err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return zero, fmt.Errorf("session_validate: token no encontrado")
|
|
}
|
|
return zero, fmt.Errorf("session_validate: scan: %w", err)
|
|
}
|
|
|
|
if time.Now().Unix() >= expiresAt {
|
|
return zero, fmt.Errorf("session_validate: sesion expirada")
|
|
}
|
|
|
|
var metadata map[string]any
|
|
if metaStr != "" {
|
|
if err := json.Unmarshal([]byte(metaStr), &metadata); err != nil {
|
|
return zero, fmt.Errorf("session_validate: metadata malformada: %w", err)
|
|
}
|
|
}
|
|
|
|
return Session{
|
|
Token: token,
|
|
UserID: userID,
|
|
ExpiresAt: expiresAt,
|
|
CreatedAt: createdAt,
|
|
Metadata: metadata,
|
|
}, nil
|
|
}
|