Files
eng-pad-server/internal/auth/users.go
Kyle Isom 286b886c06 Implement Phase 2: password auth (Argon2id + bearer tokens)
- Argon2id password hashing and verification with configurable params
- Bearer token generation (32-byte random), SHA-256 hashed storage,
  TTL-based expiry
- User creation and authentication helpers
- auth_tokens table added to migrations
- 6 tests: hash/verify, wrong password, create/auth user, token
  create/validate, token expiry

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 19:49:07 -07:00

49 lines
1.1 KiB
Go

package auth
import (
"database/sql"
"fmt"
"time"
)
// CreateUser creates a new user with a hashed password.
func CreateUser(database *sql.DB, username, password string, params Argon2Params) (int64, error) {
hash, err := HashPassword(password, params)
if err != nil {
return 0, err
}
now := time.Now().UnixMilli()
res, err := database.Exec(
"INSERT INTO users (username, password_hash, created_at, updated_at) VALUES (?, ?, ?, ?)",
username, hash, now, now,
)
if err != nil {
return 0, fmt.Errorf("insert user: %w", err)
}
return res.LastInsertId()
}
// AuthenticateUser verifies username/password and returns the user ID.
func AuthenticateUser(database *sql.DB, username, password string) (int64, error) {
var userID int64
var hash string
err := database.QueryRow(
"SELECT id, password_hash FROM users WHERE username = ?", username,
).Scan(&userID, &hash)
if err != nil {
return 0, fmt.Errorf("user not found")
}
ok, err := VerifyPassword(password, hash)
if err != nil {
return 0, err
}
if !ok {
return 0, fmt.Errorf("invalid password")
}
return userID, nil
}