- 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>
49 lines
1.1 KiB
Go
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
|
|
}
|