@@ -19,6 +19,7 @@ import (
1919 "github.com/cockroachdb/cockroach/pkg/ccl/jwtauthccl"
2020 "github.com/cockroachdb/cockroach/pkg/ccl/utilccl"
2121 "github.com/cockroachdb/cockroach/pkg/roachpb"
22+ "github.com/cockroachdb/cockroach/pkg/security/provisioning"
2223 secuser "github.com/cockroachdb/cockroach/pkg/security/username"
2324 "github.com/cockroachdb/cockroach/pkg/server"
2425 "github.com/cockroachdb/cockroach/pkg/server/authserver"
@@ -44,6 +45,7 @@ const (
4445 codeKey = "code"
4546 stateKey = "state"
4647 secretCookieName = "oidc_secret"
48+ oidcProvisioningKey = "oidc"
4749 oidcLoginPath = "/oidc/v1/login"
4850 oidcCallbackPath = "/oidc/v1/callback"
4951 oidcJWTPath = "/oidc/v1/jwt"
@@ -158,6 +160,7 @@ type oidcAuthenticationConf struct {
158160 authZEnabled bool
159161 groupClaim string
160162 userinfoGroupKey string
163+ provisioningEnabled bool
161164}
162165
163166// GetOIDCConf is used to extract certain parts of the OIDC
@@ -420,9 +423,10 @@ func reloadConfigLocked(
420423 httputil .WithDialerTimeout (clientTimeout ),
421424 httputil .WithCustomCAPEM (OIDCProviderCustomCA .Get (& st .SV )),
422425 ),
423- authZEnabled : OIDCAuthZEnabled .Get (& st .SV ),
424- groupClaim : OIDCAuthGroupClaim .Get (& st .SV ),
425- userinfoGroupKey : OIDCAuthUserinfoGroupKey .Get (& st .SV ),
426+ authZEnabled : OIDCAuthZEnabled .Get (& st .SV ),
427+ groupClaim : OIDCAuthGroupClaim .Get (& st .SV ),
428+ userinfoGroupKey : OIDCAuthUserinfoGroupKey .Get (& st .SV ),
429+ provisioningEnabled : provisioning .ClusterProvisioningConfig (st ).Enabled ("oidc" ),
426430 }
427431
428432 if ! oidcAuthServer .conf .enabled && conf .enabled {
@@ -490,6 +494,69 @@ func getRegionSpecificRedirectURL(locality roachpb.Locality, conf redirectURLCon
490494 return s , nil
491495}
492496
497+ // maybeProvisionUserLocked checks the cached OIDC configuration to see whether
498+ // automatic user provisioning is enabled. If so, it attempts to create a SQL
499+ // user linked to the OIDC identity provider.
500+ //
501+ // This function is called after a successful OIDC token exchange and
502+ // verification. Its execution relies on the success of the underlying OIDC
503+ // library, which operates on the following assumptions:
504+ //
505+ // 1. OIDC Discovery: The library uses the OIDC discovery protocol to fetch the
506+ // provider's configuration from "/.well-known/openid-configuration". This
507+ // assumes the provider has discovery enabled and accessible.
508+ //
509+ // 2. Issuer Validation: The go-oidc library's verifier ensures the 'iss' claim
510+ // in the ID Token matches the issuer URL from the discovery document. There
511+ // is also an exception made to this in go-oidc for accounts.google.com
512+ //
513+ // Errors during username validation, provisioning source parsing, or user creation
514+ // are logged with detailed context and result in an HTTP 500 response with a
515+ // generic error message to the client.
516+ func maybeProvisionUserLocked (
517+ ctx context.Context ,
518+ authConf oidcAuthenticationConf ,
519+ execCfg * sql.ExecutorConfig ,
520+ username string ,
521+ w http.ResponseWriter ,
522+ ) (err error ) {
523+ if ! authConf .provisioningEnabled {
524+ return
525+ }
526+
527+ log .Dev .Infof (ctx , "OIDC: attempting user provisioning for %s" , username )
528+ telemetry .Inc (provisioning .BeginOIDCProvisionUseCounter )
529+
530+ // Convert the extracted username string to a username.SQLUsername type.
531+ sqlUsername , err := secuser .MakeSQLUsernameFromUserInput (username , secuser .PurposeCreation )
532+ if err != nil {
533+ log .Dev .Errorf (ctx , "OIDC provisioning: invalid username format for %s: %v" , username , err )
534+ http .Error (w , "OIDC: invalid username format" , http .StatusInternalServerError )
535+ return err
536+ }
537+
538+ // Create the provisioning source identifier string, e.g., "oidc:https://accounts.example.com".
539+ idpString := oidcProvisioningKey + ":" + authConf .providerURL
540+ provisioningSource , err := provisioning .ParseProvisioningSource (idpString )
541+ if err != nil {
542+ // This error occurs if the provisioning package doesn't recognize the "oidc:" prefix.
543+ log .Dev .Errorf (ctx , "OIDC provisioning: error parsing provisioning source IDP %s: %v" , idpString , err )
544+ http .Error (w , "OIDC: provisioning error" , http .StatusInternalServerError )
545+ return err
546+ }
547+
548+ // Call the core provisioning function using the execCfg.
549+ if err = sql .CreateRoleForProvisioning (ctx , execCfg , sqlUsername , provisioningSource .String ()); err != nil {
550+ log .Dev .Errorf (ctx , "OIDC provisioning: error provisioning user %s: %v" , sqlUsername , err )
551+ http .Error (w , "OIDC: provisioning error" , http .StatusInternalServerError )
552+ return err
553+ }
554+
555+ log .Dev .Infof (ctx , "OIDC: successfully provisioned user %s" , sqlUsername )
556+ telemetry .Inc (provisioning .ProvisionOIDCSuccessCounter )
557+ return
558+ }
559+
493560// ConfigureOIDC attaches handlers to the server `mux` that
494561// can initiate and complete an OIDC authentication flow.
495562// This flow consists of an initial login request that triggers
@@ -608,6 +675,12 @@ var ConfigureOIDC = func(
608675 return
609676 }
610677
678+ // OIDC user provisioning
679+ if err := maybeProvisionUserLocked (ctx , oidcAuthentication .conf , oidcAuthentication .execCfg , username , w ); err != nil {
680+ log .Dev .Errorf (ctx , "OIDC provisioning failed with error: %v" , err )
681+ return
682+ }
683+
611684 // OIDC authorization
612685 if err := oidcAuthentication .authorize (ctx , rawIDToken , rawAccessToken , username ); err != nil {
613686 log .Dev .Errorf (ctx , "OIDC authorization failed with error: %v" , err )
@@ -938,6 +1011,9 @@ var ConfigureOIDC = func(
9381011 OIDCAuthUserinfoGroupKey .SetOnChange (& st .SV , func (ctx context.Context ) {
9391012 reloadConfig (ambientCtx .AnnotateCtx (ctx ), oidcAuthentication , locality , st )
9401013 })
1014+ provisioning .OIDCProvisioningEnabled .SetOnChange (& st .SV , func (ctx context.Context ) {
1015+ reloadConfig (ambientCtx .AnnotateCtx (ctx ), oidcAuthentication , locality , st )
1016+ })
9411017
9421018 return oidcAuthentication , nil
9431019}
0 commit comments