@@ -344,6 +344,170 @@ func ComputeTokenRenewalTimeWithRatio(creationTime, expirationTime time.Time, ra
344344 return renewalAt
345345}
346346
347+ // CreateOIDCKubeconfig creates a kubeconfig that uses the oidc-login plugin for authentication.
348+ // The 'user' arg is used as key for the auth configuration and can be chosen freely.
349+ // Note that this kubeconfig is meant for human users, controllers can usually not execute 'kubectl oidc-login get-token'.
350+ func CreateOIDCKubeconfig (user , host string , caData []byte , issuer , clientID string , extraOptions ... CreateOIDCKubeconfigOption ) ([]byte , error ) {
351+ opts := & CreateOIDCKubeconfigOptions {
352+ User : user ,
353+ Host : host ,
354+ CAData : caData ,
355+ Issuer : issuer ,
356+ ClientID : clientID ,
357+ ContextName : "cluster" ,
358+ ClusterName : "cluster" ,
359+ }
360+
361+ for _ , apply := range extraOptions {
362+ apply (opts )
363+ }
364+
365+ return createOIDCKubeconfig (opts )
366+ }
367+
368+ func createOIDCKubeconfig (opts * CreateOIDCKubeconfigOptions ) ([]byte , error ) {
369+ grantType := opts .GrantType
370+ if grantType == "" {
371+ grantType = GrantTypeAuto
372+ }
373+ exec := & clientcmdapi.ExecConfig {
374+ APIVersion : "client.authentication.k8s.io/v1beta1" ,
375+ Command : "kubectl" ,
376+ Args : []string {
377+ "oidc-login" ,
378+ "get-token" ,
379+ "--grant-type=" + string (grantType ),
380+ "--oidc-issuer-url=" + opts .Issuer ,
381+ "--oidc-client-id=" + opts .ClientID ,
382+ },
383+ }
384+ if opts .ClientSecret != "" {
385+ exec .Args = append (exec .Args , "--oidc-client-secret=" + opts .ClientSecret )
386+ }
387+ for _ , extraScope := range opts .ExtraScopes {
388+ exec .Args = append (exec .Args , "--oidc-extra-scope=" + extraScope )
389+ }
390+ if opts .UsePKCE {
391+ exec .Args = append (exec .Args , "--oidc-use-pkce" )
392+ }
393+ if opts .ForceRefresh {
394+ exec .Args = append (exec .Args , "--force-refresh" )
395+ }
396+
397+ kcfg := clientcmdapi.Config {
398+ APIVersion : "v1" ,
399+ Kind : "Config" ,
400+ Clusters : map [string ]* clientcmdapi.Cluster {
401+ opts .ClusterName : {
402+ Server : opts .Host ,
403+ CertificateAuthorityData : opts .CAData ,
404+ },
405+ },
406+ Contexts : map [string ]* clientcmdapi.Context {
407+ opts .ContextName : {
408+ Cluster : opts .ClusterName ,
409+ AuthInfo : opts .User ,
410+ },
411+ },
412+ CurrentContext : opts .ContextName ,
413+ AuthInfos : map [string ]* clientcmdapi.AuthInfo {
414+ opts .User : {
415+ Exec : exec ,
416+ },
417+ },
418+ }
419+
420+ kcfgBytes , err := clientcmd .Write (kcfg )
421+ if err != nil {
422+ return nil , fmt .Errorf ("error converting converting generated kubeconfig into yaml: %w" , err )
423+ }
424+ return kcfgBytes , nil
425+ }
426+
427+ type CreateOIDCKubeconfigOptions struct {
428+ ContextName string
429+ ClusterName string
430+ User string
431+ Host string
432+ CAData []byte
433+ Issuer string
434+ ClientID string
435+ ClientSecret string
436+ ExtraScopes []string
437+ UsePKCE bool
438+ ForceRefresh bool
439+ GrantType OIDCGrantType
440+ }
441+
442+ type OIDCGrantType string
443+
444+ const (
445+ GrantTypeAuto OIDCGrantType = "auto"
446+ GrantTypeAuthCode OIDCGrantType = "authcode"
447+ GrantTypeAuthCodeKeyboard OIDCGrantType = "authcode-keyboard"
448+ GrantTypePassword OIDCGrantType = "password"
449+ GrantTypeDeviceCode OIDCGrantType = "device-code"
450+ )
451+
452+ type CreateOIDCKubeconfigOption func (* CreateOIDCKubeconfigOptions )
453+
454+ // WithExtraScope is an option for CreateOIDCKubeconfig that adds an extra scope to the oidc-login subcommand.
455+ // This option can be used multiple times to add multiple scopes.
456+ func WithExtraScope (scope string ) CreateOIDCKubeconfigOption {
457+ return func (opts * CreateOIDCKubeconfigOptions ) {
458+ opts .ExtraScopes = append (opts .ExtraScopes , scope )
459+ }
460+ }
461+
462+ // UsePKCE is an option for CreateOIDCKubeconfig that enforces the use of PKCE.
463+ func UsePKCE () CreateOIDCKubeconfigOption {
464+ return func (opts * CreateOIDCKubeconfigOptions ) {
465+ opts .UsePKCE = true
466+ }
467+ }
468+
469+ // ForceRefresh is an option for CreateOIDCKubeconfig that forces the refresh of the token, independent of its expiration time.
470+ func ForceRefresh () CreateOIDCKubeconfigOption {
471+ return func (opts * CreateOIDCKubeconfigOptions ) {
472+ opts .ForceRefresh = true
473+ }
474+ }
475+
476+ // WithGrantType is an option for CreateOIDCKubeconfig that sets the grant type.
477+ // Valid values are "auto", "authcode", "authcode-keyboard", "password", and "device-code".
478+ func WithGrantType (grantType OIDCGrantType ) CreateOIDCKubeconfigOption {
479+ return func (opts * CreateOIDCKubeconfigOptions ) {
480+ opts .GrantType = grantType
481+ }
482+ }
483+
484+ // WithClientSecret is an option for CreateOIDCKubeconfig that sets the client secret.
485+ func WithClientSecret (clientSecret string ) CreateOIDCKubeconfigOption {
486+ return func (opts * CreateOIDCKubeconfigOptions ) {
487+ opts .ClientSecret = clientSecret
488+ }
489+ }
490+
491+ // WithContextName allows to override the default context name "cluster" in the kubeconfig.
492+ func WithContextName (contextName string ) CreateOIDCKubeconfigOption {
493+ return func (opts * CreateOIDCKubeconfigOptions ) {
494+ opts .ContextName = contextName
495+ if opts .ContextName == "" {
496+ opts .ContextName = "cluster"
497+ }
498+ }
499+ }
500+
501+ // WithClusterName allows to override the default cluster name "cluster" in the kubeconfig.
502+ func WithClusterName (clusterName string ) CreateOIDCKubeconfigOption {
503+ return func (opts * CreateOIDCKubeconfigOptions ) {
504+ opts .ClusterName = clusterName
505+ if opts .ClusterName == "" {
506+ opts .ClusterName = "cluster"
507+ }
508+ }
509+ }
510+
347511// oidcTrustConfig represents the configuration for an OIDC trust relationship.
348512// It includes the host of the Kubernetes API server, CA data for TLS verification,
349513// and the audience for the OIDC tokens.
0 commit comments