[management] enable optional zitadel configuration of a PAT (#3159)

* [management] enable optional zitadel configuration of a PAT for service user via the ExtraConfig fields

* [management] validate both PAT and JWT configurations for zitadel
This commit is contained in:
adasauce 2025-01-14 05:38:08 -04:00 committed by GitHub
parent 522dd44bfa
commit 0c28099712
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 48 additions and 14 deletions

View File

@ -149,6 +149,7 @@ func NewManager(ctx context.Context, config Config, appMetrics telemetry.AppMetr
GrantType: config.ClientConfig.GrantType, GrantType: config.ClientConfig.GrantType,
TokenEndpoint: config.ClientConfig.TokenEndpoint, TokenEndpoint: config.ClientConfig.TokenEndpoint,
ManagementEndpoint: config.ExtraConfig["ManagementEndpoint"], ManagementEndpoint: config.ExtraConfig["ManagementEndpoint"],
PAT: config.ExtraConfig["PAT"],
} }
} }

View File

@ -34,6 +34,7 @@ type ZitadelClientConfig struct {
GrantType string GrantType string
TokenEndpoint string TokenEndpoint string
ManagementEndpoint string ManagementEndpoint string
PAT string
} }
// ZitadelCredentials zitadel authentication information. // ZitadelCredentials zitadel authentication information.
@ -135,6 +136,28 @@ func readZitadelError(body io.ReadCloser) error {
return errors.New(strings.Join(errsOut, " ")) return errors.New(strings.Join(errsOut, " "))
} }
// verifyJWTConfig ensures necessary values are set in the ZitadelClientConfig for JWTs to be generated.
func verifyJWTConfig(config ZitadelClientConfig) error {
if config.ClientID == "" {
return fmt.Errorf("zitadel IdP configuration is incomplete, clientID is missing")
}
if config.ClientSecret == "" {
return fmt.Errorf("zitadel IdP configuration is incomplete, ClientSecret is missing")
}
if config.TokenEndpoint == "" {
return fmt.Errorf("zitadel IdP configuration is incomplete, TokenEndpoint is missing")
}
if config.GrantType == "" {
return fmt.Errorf("zitadel IdP configuration is incomplete, GrantType is missing")
}
return nil
}
// NewZitadelManager creates a new instance of the ZitadelManager. // NewZitadelManager creates a new instance of the ZitadelManager.
func NewZitadelManager(config ZitadelClientConfig, appMetrics telemetry.AppMetrics) (*ZitadelManager, error) { func NewZitadelManager(config ZitadelClientConfig, appMetrics telemetry.AppMetrics) (*ZitadelManager, error) {
httpTransport := http.DefaultTransport.(*http.Transport).Clone() httpTransport := http.DefaultTransport.(*http.Transport).Clone()
@ -146,26 +169,18 @@ func NewZitadelManager(config ZitadelClientConfig, appMetrics telemetry.AppMetri
} }
helper := JsonParser{} helper := JsonParser{}
if config.ClientID == "" { hasPAT := config.PAT != ""
return nil, fmt.Errorf("zitadel IdP configuration is incomplete, clientID is missing") if !hasPAT {
} jwtErr := verifyJWTConfig(config)
if jwtErr != nil {
if config.ClientSecret == "" { return nil, jwtErr
return nil, fmt.Errorf("zitadel IdP configuration is incomplete, ClientSecret is missing") }
}
if config.TokenEndpoint == "" {
return nil, fmt.Errorf("zitadel IdP configuration is incomplete, TokenEndpoint is missing")
} }
if config.ManagementEndpoint == "" { if config.ManagementEndpoint == "" {
return nil, fmt.Errorf("zitadel IdP configuration is incomplete, ManagementEndpoint is missing") return nil, fmt.Errorf("zitadel IdP configuration is incomplete, ManagementEndpoint is missing")
} }
if config.GrantType == "" {
return nil, fmt.Errorf("zitadel IdP configuration is incomplete, GrantType is missing")
}
credentials := &ZitadelCredentials{ credentials := &ZitadelCredentials{
clientConfig: config, clientConfig: config,
httpClient: httpClient, httpClient: httpClient,
@ -254,6 +269,20 @@ func (zc *ZitadelCredentials) parseRequestJWTResponse(rawBody io.ReadCloser) (JW
return jwtToken, nil return jwtToken, nil
} }
// generatePATToken creates a functional JWTToken instance which will pass the
// PAT to the API directly and skip requesting a token.
func (zc *ZitadelCredentials) generatePATToken() (JWTToken, error) {
tok := JWTToken{
AccessToken: zc.clientConfig.PAT,
Scope: "openid",
ExpiresIn: 9999,
TokenType: "PAT",
}
tok.expiresInTime = time.Now().Add(time.Duration(tok.ExpiresIn) * time.Second)
zc.jwtToken = tok
return tok, nil
}
// Authenticate retrieves access token to use the Zitadel Management API. // Authenticate retrieves access token to use the Zitadel Management API.
func (zc *ZitadelCredentials) Authenticate(ctx context.Context) (JWTToken, error) { func (zc *ZitadelCredentials) Authenticate(ctx context.Context) (JWTToken, error) {
zc.mux.Lock() zc.mux.Lock()
@ -269,6 +298,10 @@ func (zc *ZitadelCredentials) Authenticate(ctx context.Context) (JWTToken, error
return zc.jwtToken, nil return zc.jwtToken, nil
} }
if zc.clientConfig.PAT != "" {
return zc.generatePATToken()
}
resp, err := zc.requestJWTToken(ctx) resp, err := zc.requestJWTToken(ctx)
if err != nil { if err != nil {
return zc.jwtToken, err return zc.jwtToken, err