Files
silo-server/internal/auth/provider.go
T

83 lines
2.5 KiB
Go

package auth
import (
"context"
"errors"
"fmt"
"github.com/Silo-Server/silo-server/internal/models"
)
// Sentinel errors for authentication operations.
var (
ErrInvalidCredentials = errors.New("invalid credentials")
ErrUserDisabled = errors.New("user account is disabled")
)
// Credentials holds the username and password for authentication.
type Credentials struct {
Username string
Password string
}
// AuthProvider is the interface for pluggable authentication backends.
// Implementations validate user credentials and session state.
type AuthProvider interface {
// Authenticate validates the given credentials and returns the
// authenticated user. Returns ErrInvalidCredentials if the username
// does not exist or the password is wrong. Returns ErrUserDisabled
// if the account is disabled.
Authenticate(ctx context.Context, credentials Credentials) (*models.User, error)
// ValidateSession checks whether the given session ID is still valid
// (exists, not revoked, not expired).
ValidateSession(ctx context.Context, sessionID string) (bool, error)
}
// LocalProvider authenticates users against the local PostgreSQL database
// using bcrypt password hashing.
type LocalProvider struct {
users *UserRepository
sessions *SessionRepository
}
// NewLocalProvider creates a new LocalProvider backed by the given repositories.
func NewLocalProvider(users *UserRepository, sessions *SessionRepository) *LocalProvider {
return &LocalProvider{
users: users,
sessions: sessions,
}
}
// Authenticate validates the username/password pair against the database.
// Returns ErrInvalidCredentials if the user is not found or the password
// does not match. Returns ErrUserDisabled if the user's account is disabled.
func (p *LocalProvider) Authenticate(ctx context.Context, creds Credentials) (*models.User, error) {
user, err := p.users.GetByUsername(ctx, creds.Username)
if err != nil {
if IsNotFound(err) {
return nil, ErrInvalidCredentials
}
return nil, fmt.Errorf("looking up user: %w", err)
}
if !user.LocalPasswordLoginEnabled {
return nil, ErrInvalidCredentials
}
if !CheckPassword(user, creds.Password) {
return nil, ErrInvalidCredentials
}
if !user.Enabled {
return nil, ErrUserDisabled
}
return user, nil
}
// ValidateSession checks whether the session identified by sessionID is
// currently valid (exists, not revoked, not expired).
func (p *LocalProvider) ValidateSession(ctx context.Context, sessionID string) (bool, error) {
return p.sessions.IsValid(ctx, sessionID)
}