From 1dec8ed74034b1078fcefb675a29bf2baf44dfd2 Mon Sep 17 00:00:00 2001 From: lkc8fe Date: Mon, 10 Nov 2025 11:46:59 +0100 Subject: [PATCH] feat: allow OIDC providers which do not have `email_verified` claim This fix allows using OIDC login for Azure or other similar Auth Systems. Fixes: https://github.com/siderolabs/omni/issues/1818 Signed-off-by: lkc8fe --- cmd/omni/cmd/cmd.go | 3 +++ internal/backend/server.go | 1 + internal/pkg/auth/oidc/token.go | 15 ++++++++++----- internal/pkg/config/auth.go | 13 +++++++------ 4 files changed, 21 insertions(+), 11 deletions(-) diff --git a/cmd/omni/cmd/cmd.go b/cmd/omni/cmd/cmd.go index 03928840..5aee7c00 100644 --- a/cmd/omni/cmd/cmd.go +++ b/cmd/omni/cmd/cmd.go @@ -475,6 +475,9 @@ func defineAuthFlags() { rootCmd.Flags().StringVar(&cmdConfig.Auth.OIDC.LogoutURL, "auth-oidc-logout-url", cmdConfig.Auth.OIDC.LogoutURL, "OIDC logout URL.") + + rootCmd.Flags().BoolVar(&cmdConfig.Auth.OIDC.AllowUnverifiedEmail, "auth-oidc-allow-unverified-email", cmdConfig.Auth.OIDC.AllowUnverifiedEmail, + "Allow OIDC tokens without email_verified claim.") } func defineLogsFlags() { diff --git a/internal/backend/server.go b/internal/backend/server.go index f8349df4..8fbb7e7e 100644 --- a/internal/backend/server.go +++ b/internal/backend/server.go @@ -511,6 +511,7 @@ func (s *Server) getAuthInterceptors(ctx context.Context) ([]interceptorCreator, ctx, s.oidcProvider, config.Config.Auth.OIDC.ClientID, + config.Config.Auth.OIDC.AllowUnverifiedEmail, ) if err != nil { return nil, err diff --git a/internal/pkg/auth/oidc/token.go b/internal/pkg/auth/oidc/token.go index 2de71942..1564a55d 100644 --- a/internal/pkg/auth/oidc/token.go +++ b/internal/pkg/auth/oidc/token.go @@ -9,6 +9,7 @@ import ( "context" "fmt" "net/mail" + "strings" "github.com/coreos/go-oidc/v3/oidc" "github.com/siderolabs/go-api-signature/pkg/jwt" @@ -16,17 +17,21 @@ import ( // IDTokenVerifier is an Auth0 ID token verifier. type IDTokenVerifier struct { - verifier *oidc.IDTokenVerifier + verifier *oidc.IDTokenVerifier + allowUnverifiedEmail bool } // NewIDTokenVerifier creates a new ID token verifier. -func NewIDTokenVerifier(ctx context.Context, provider *oidc.Provider, clientID string) (*IDTokenVerifier, error) { +func NewIDTokenVerifier(ctx context.Context, provider *oidc.Provider, clientID string, + allowUnverifiedEmail bool, +) (*IDTokenVerifier, error) { verifier := provider.Verifier(&oidc.Config{ ClientID: clientID, }) return &IDTokenVerifier{ - verifier: verifier, + verifier: verifier, + allowUnverifiedEmail: allowUnverifiedEmail, }, nil } @@ -51,12 +56,12 @@ func (v *IDTokenVerifier) Verify(ctx context.Context, token string) (*jwt.Claims return nil, fmt.Errorf("email claim is not valid: %w: %s", err, claims.Email) } - if !claims.EmailVerified { + if !claims.EmailVerified && !v.allowUnverifiedEmail { return nil, &EmailNotVerifiedError{Email: claims.Email} } return &jwt.Claims{ - VerifiedEmail: claims.Email, + VerifiedEmail: strings.ToLower(claims.Email), }, nil } diff --git a/internal/pkg/config/auth.go b/internal/pkg/config/auth.go index b099ace6..0219ddb7 100644 --- a/internal/pkg/config/auth.go +++ b/internal/pkg/config/auth.go @@ -62,12 +62,13 @@ type WebAuthn struct { // OIDC holds the configuration parameters for OIDC auth. type OIDC struct { - ProviderURL string `yaml:"providerURL"` - ClientID string `yaml:"clientID"` - ClientSecret string `yaml:"clientSecret"` - LogoutURL string `yaml:"logoutURL"` - Scopes []string `yaml:"scopes"` - Enabled bool `yaml:"enabled"` + ProviderURL string `yaml:"providerURL"` + ClientID string `yaml:"clientID"` + ClientSecret string `yaml:"clientSecret"` + LogoutURL string `yaml:"logoutURL"` + Scopes []string `yaml:"scopes"` + Enabled bool `yaml:"enabled"` + AllowUnverifiedEmail bool `yaml:"allowUnverifiedEmail"` } // SAML holds configuration parameters for SAML auth.