From 711dbbda2fc5ed92d3e7c1467e301d615e19397f Mon Sep 17 00:00:00 2001 From: Cyril David Date: Fri, 23 Jul 2021 15:35:04 -0700 Subject: [PATCH] feat: add client credentials flow (#304) * feat: add MVP for client credentials flow This would potentially support private cloud customers * Don't require login on tenant add * Update internal/cli/tenants.go Co-authored-by: Rita Zerrizuela --- internal/cli/cli.go | 30 +++++++++++++++++-- internal/cli/root.go | 2 +- internal/cli/tenants.go | 66 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+), 4 deletions(-) diff --git a/internal/cli/cli.go b/internal/cli/cli.go index 3eeb2e0de..222f1fd8f 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -51,6 +51,9 @@ type tenant struct { ExpiresAt time.Time `json:"expires_at"` Apps map[string]app `json:"apps,omitempty"` DefaultAppID string `json:"default_app_id,omitempty"` + + ClientID string `json:"client_id"` + ClientSecret string `json:"client_secret"` } type app struct { @@ -132,9 +135,23 @@ func (c *cli) setup(ctx context.Context) error { return err } - m, err := management.New(t.Domain, - management.WithStaticToken(t.AccessToken), - management.WithUserAgent(fmt.Sprintf("%v/%v", userAgent, strings.TrimPrefix(buildinfo.Version, "v")))) + var ( + m *management.Management + ua = fmt.Sprintf("%v/%v", userAgent, strings.TrimPrefix(buildinfo.Version, "v")) + ) + + if t.ClientID != "" && t.ClientSecret != "" { + m, err = management.New(t.Domain, + management.WithClientCredentials(t.ClientID, t.ClientSecret), + management.WithUserAgent(ua), + ) + } else { + m, err = management.New(t.Domain, + management.WithStaticToken(t.AccessToken), + management.WithUserAgent(ua), + ) + } + if err != nil { return err } @@ -153,6 +170,10 @@ func (c *cli) prepareTenant(ctx context.Context) (tenant, error) { return tenant{}, err } + if t.ClientID != "" && t.ClientSecret != "" { + return t, nil + } + if t.AccessToken == "" || scopesChanged(t) { t, err = RunLogin(ctx, c, true) if err != nil { @@ -167,6 +188,9 @@ func (c *cli) prepareTenant(ctx context.Context) (tenant, error) { Client: http.DefaultClient, } + // NOTE(cyx): this code will have to be adapted to instead + // maybe take the clientID/secret as additional params, or + // something similar. res, err := tr.Refresh(ctx, t.Domain) if err != nil { // ask and guide the user through the login process: diff --git a/internal/cli/root.go b/internal/cli/root.go index bb0ed5065..43e339bf7 100644 --- a/internal/cli/root.go +++ b/internal/cli/root.go @@ -127,7 +127,7 @@ func buildRootCmd(cli *cli) *cobra.Command { } // Selecting tenants shouldn't really trigger a login. - if cmd.Use == "use" && cmd.Parent().Use == "tenants" { + if cmd.Parent().Use == "tenants" && (cmd.Use == "use" || cmd.Use == "add") { return nil } diff --git a/internal/cli/tenants.go b/internal/cli/tenants.go index d0969533b..10e1b8a2a 100644 --- a/internal/cli/tenants.go +++ b/internal/cli/tenants.go @@ -12,6 +12,22 @@ var ( Name: "Tenant", Help: "Tenant to select", } + + tenantClientID = Flag{ + Name: "Client ID", + LongForm: "client-id", + ShortForm: "i", + Help: "Client ID of the application.", + IsRequired: true, + } + + tenantClientSecret = Flag{ + Name: "Client Secret", + LongForm: "client-secret", + ShortForm: "s", + Help: "Client Secret of the application.", + IsRequired: true, + } ) func tenantsCmd(cli *cli) *cobra.Command { @@ -25,6 +41,7 @@ func tenantsCmd(cli *cli) *cobra.Command { cmd.AddCommand(useTenantCmd(cli)) cmd.AddCommand(listTenantCmd(cli)) cmd.AddCommand(openTenantCmd(cli)) + cmd.AddCommand(addTenantCmd(cli)) return cmd } @@ -132,6 +149,55 @@ func openTenantCmd(cli *cli) *cobra.Command { return cmd } +func addTenantCmd(cli *cli) *cobra.Command { + var inputs struct { + Domain string + ClientID string + ClientSecret string + } + + cmd := &cobra.Command{ + Use: "add", + Args: cobra.MaximumNArgs(1), + Short: "Add a tenant with client credentials", + Long: "Add a tenant with client credentials.", + Example: "auth0 tenants add --client-id --client-secret ", + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + err := tenantDomain.Pick(cmd, &inputs.Domain, cli.tenantPickerOptions) + if err != nil { + return err + } + } else { + inputs.Domain = args[0] + } + + if err := tenantClientID.Ask(cmd, &inputs.ClientID, nil); err != nil { + return err + } + + if err := tenantClientSecret.Ask(cmd, &inputs.ClientSecret, nil); err != nil { + return err + } + + t := tenant{ + Domain: inputs.Domain, + ClientID: inputs.ClientID, + ClientSecret: inputs.ClientSecret, + } + + if err := cli.addTenant(t); err != nil { + return err + } + + cli.renderer.Infof("Tenant added successfully: %s", t.Domain) + return nil + }, + } + + return cmd +} + func (c *cli) tenantPickerOptions() (pickerOptions, error) { tens, err := c.listTenants() if err != nil {