120 lines
3.6 KiB
Go
120 lines
3.6 KiB
Go
package auth
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
|
|
"trankilou.fr/lassistanoque/backend/internal/domain"
|
|
)
|
|
|
|
var (
|
|
ErrRegistrationNotAllowed = errors.New("registration not allowed")
|
|
ErrMethodUnknown = errors.New("auth method unknown")
|
|
ErrUnauthorized = errors.New("unauthorized")
|
|
)
|
|
|
|
type Service struct {
|
|
authenticators map[string]Authenticator // "password" -> ..., "oidc" -> ...
|
|
oidcManager OIDCManager
|
|
tokenManager TokenManager
|
|
userRepository domain.UserRepository
|
|
oidcRepository domain.OIDCProviderRepository
|
|
settingsRepository domain.SettingsRepository
|
|
}
|
|
|
|
func NewService(
|
|
settingsRepository domain.SettingsRepository,
|
|
userRepository domain.UserRepository,
|
|
oidcRepository domain.OIDCProviderRepository,
|
|
tokenManager TokenManager,
|
|
authenticators map[string]Authenticator,
|
|
) *Service {
|
|
service := &Service{
|
|
authenticators: authenticators,
|
|
tokenManager: tokenManager,
|
|
userRepository: userRepository,
|
|
oidcRepository: oidcRepository,
|
|
settingsRepository: settingsRepository,
|
|
}
|
|
if manager, ok := authenticators["oidc"].(OIDCManager); ok {
|
|
service.oidcManager = manager
|
|
}
|
|
return service
|
|
}
|
|
|
|
func (s *Service) Login(ctx context.Context, creds Credentials) (*Session, error) {
|
|
authn, ok := s.authenticators[creds.Method]
|
|
if !ok {
|
|
return nil, fmt.Errorf("auth method unsupported: %s", creds.Method)
|
|
}
|
|
return authn.Authenticate(ctx, creds)
|
|
}
|
|
|
|
func (s *Service) Register(registration Registration) (*Session, error) {
|
|
settings, err := s.settingsRepository.GetSettings()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !settings.RegisterEnabled {
|
|
return nil, ErrRegistrationNotAllowed
|
|
}
|
|
if authenticator, ok := s.authenticators[registration.Method]; ok {
|
|
session, err := authenticator.Register(context.Background(), registration)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
now := time.Now()
|
|
_, err = s.userRepository.CreateTeam(session.User.ID, &domain.Team{
|
|
Label: "Espace personnel",
|
|
DateCreated: now,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return session, nil
|
|
} else {
|
|
return nil, ErrMethodUnknown
|
|
}
|
|
}
|
|
|
|
func (s *Service) Status(session *Session) (*domain.User, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
// ListOIDCProviders retourne les fournisseurs OpenID Connect enregistrés,
|
|
// sans exposer le secret client (champ marqué json:"-").
|
|
func (s *Service) ListOIDCProviders() ([]*domain.OIDCProvider, error) {
|
|
return s.oidcRepository.ListOIDCProviders()
|
|
}
|
|
|
|
// OIDCAuthorizeURL construit l'URL d'autorisation du fournisseur, avec un
|
|
// state signé transportant l'id du fournisseur.
|
|
func (s *Service) OIDCAuthorizeURL(ctx context.Context, providerID string, redirectURI string) (string, error) {
|
|
provider, err := s.oidcRepository.FindOIDCProvider(providerID)
|
|
if err != nil {
|
|
return "", fmt.Errorf("unknown oidc provider: %s", providerID)
|
|
}
|
|
state, err := s.tokenManager.GenerateStateToken(providerID)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return s.oidcManager.AuthorizeURL(ctx, provider, redirectURI, state)
|
|
}
|
|
|
|
// OIDCCallback traite le retour du fournisseur: vérifie le state, échange le
|
|
// code d'autorisation et délivre une session applicative.
|
|
func (s *Service) OIDCCallback(ctx context.Context, state string, code string, redirectURI string) (*Session, error) {
|
|
providerID, err := s.tokenManager.ParseStateToken(state)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid oidc state: %s", err)
|
|
}
|
|
provider, err := s.oidcRepository.FindOIDCProvider(providerID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unknown oidc provider: %s", providerID)
|
|
}
|
|
return s.oidcManager.ExchangeCode(ctx, provider, code, redirectURI)
|
|
}
|