83 lines
2.5 KiB
Go
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)
|
|
}
|