package postgres import ( "context" "database/sql" "time" "gardomatic.kleiax.de/internal/auth" ) // TokenModel stores scoped authentication token hashes in PostgreSQL. // TokenModel persists only token hashes; plaintext tokens are returned once to // the caller and never stored. type TokenModel struct { DB *sql.DB } // New creates a token and persists only its hash. func (m TokenModel) New(userID int, ttl time.Duration, scope string) (auth.Token, error) { token := auth.NewToken(userID, ttl, scope) err := m.insert(token) return token, err } func (m TokenModel) insert(token auth.Token) error { query := ` INSERT INTO tokens (hash, user_id, expiry, scope) VALUES ($1, $2, $3, $4)` args := []any{token.Hash, token.UserID, token.Expiry, token.Scope} ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() _, err := m.DB.ExecContext(ctx, query, args...) return err } // DeleteAllForUser revokes every token for one user and scope. func (m TokenModel) DeleteAllForUser(scope string, userID int) error { query := ` DELETE FROM tokens WHERE scope = $1 AND user_id = $2` ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() _, err := m.DB.ExecContext(ctx, query, scope, userID) return err }