authentification OpenID Connect (Keycloak) : flux authorization code, state signé, validation du token ID via JWKS, provisioning automatique des utilisateurs

This commit is contained in:
fabien committed 2026-10-06 22:03:20 +02:00
1 parent 51bbb45b32
commit bedb6c98e7
15 files changed
+1057 -23

No files matched your search

+15 -4
View File
@@ -9,10 +9,12 @@ import (
)
type Credentials struct {
Method string // "password" ou "oidc"
Email string
Password string
OIDCCode string
Method string // "password" ou "oidc"
Email string
Password string
OIDCCode string
OIDCState string
RedirectURI string
}
type Registration struct {
@@ -45,6 +47,15 @@ type Authenticator interface {
type TokenManager interface {
GenerateAccessToken(user *domain.User) (string, time.Time, error)
GenerateRefreshToken(userID string) (string, time.Time, error)
GenerateStateToken(data string) (string, error)
ParseStateToken(state string) (string, error)
ParseAndValidate(tokenString string) (*domain.User, error)
TokenMiddleware(next echo.HandlerFunc) echo.HandlerFunc
}
// OIDCManager est le port implémenté par l'adaptateur OpenID Connect.
// Il gère la redirection vers le fournisseur et l'échange du code d'autorisation.
type OIDCManager interface {
AuthorizeURL(ctx context.Context, provider *domain.OIDCProvider, redirectURI string, state string) (string, error)
ExchangeCode(ctx context.Context, provider *domain.OIDCProvider, code string, redirectURI string) (*Session, error)
}
+46 -1
View File
@@ -17,20 +17,31 @@ var (
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 {
return &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) {
@@ -72,3 +83,37 @@ func (s *Service) Register(registration Registration) (*Session, error) {
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)
}