Files
2026-02-26 17:52:48 +09:00

69 lines
1.5 KiB
Go

package auth
import (
"context"
"errors"
"fmt"
"golang.org/x/crypto/bcrypt"
"a301_game_server/internal/db"
)
var (
ErrInvalidCredentials = errors.New("invalid credentials")
ErrUsernameTaken = errors.New("username already taken")
)
// Service handles account registration and authentication.
type Service struct {
pool *db.Pool
}
// NewService creates a new auth service.
func NewService(pool *db.Pool) *Service {
return &Service{pool: pool}
}
// Register creates a new account. Returns the account ID.
func (s *Service) Register(ctx context.Context, username, password string) (int64, error) {
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return 0, fmt.Errorf("hash password: %w", err)
}
var id int64
err = s.pool.QueryRow(ctx,
`INSERT INTO accounts (username, password) VALUES ($1, $2)
ON CONFLICT (username) DO NOTHING
RETURNING id`,
username, string(hash),
).Scan(&id)
if err != nil {
return 0, ErrUsernameTaken
}
return id, nil
}
// Login validates credentials and returns the account ID.
func (s *Service) Login(ctx context.Context, username, password string) (int64, error) {
var id int64
var hash string
err := s.pool.QueryRow(ctx,
`SELECT id, password FROM accounts WHERE username = $1`, username,
).Scan(&id, &hash)
if err != nil {
return 0, ErrInvalidCredentials
}
if err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)); err != nil {
return 0, ErrInvalidCredentials
}
return id, nil
}