Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DXCDT-267: Consolidate auth0 add tenants into auth0 login (1/x) #546

Merged
merged 9 commits into from
Dec 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ test-unit: ## Run unit tests
test-integration: $(GO_BIN)/commander ## Run integration tests. To run a specific test pass the FILTER var. Usage: `make test-integration FILTER="attack protection"`
${call print, "Running integration tests"}
@$(MAKE) install # ensure fresh install prior to running test
auth0 tenants add ${AUTH0_CLI_CLIENT_DOMAIN} --client-id ${AUTH0_CLI_CLIENT_ID} --client-secret ${AUTH0_CLI_CLIENT_SECRET} && commander test ./test/integration/test-cases.yaml --filter "$(FILTER)"; \
auth0 login --domain ${AUTH0_CLI_CLIENT_DOMAIN} --client-id ${AUTH0_CLI_CLIENT_ID} --client-secret ${AUTH0_CLI_CLIENT_SECRET} && commander test ./test/integration/test-cases.yaml --filter "$(FILTER)"; \
exit_code=$$?; \
bash ./test/integration/scripts/test-cleanup.sh; \
exit $$exit_code
Expand Down
14 changes: 12 additions & 2 deletions docs/auth0_login.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,26 @@ Authenticate the Auth0 CLI

### Synopsis

Sign in to your Auth0 account and authorize the CLI to access the Management API.
Authenticates the Auth0 CLI either as a user using personal credentials or as a machine using client credentials.

```
auth0 login [flags]
```

### Examples

```
auth0 login
auth0 login --domain <tenant-domain> --client-id <client-id> --client-secret <client-secret>
```

### Options

```
-h, --help help for login
--client-id string Client ID of the application when authenticating via client credentials.
--client-secret string Client secret of the application when authenticating via client credentials.
--domain string Tenant domain of the application when authenticating via client credentials.
-h, --help help for login
```

### Options inherited from parent commands
Expand Down
1 change: 0 additions & 1 deletion docs/auth0_tenants.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ Manage configured tenants.
### SEE ALSO

* [auth0](/auth0-cli/) - Supercharge your development workflow.
* [auth0 tenants add](auth0_tenants_add.md) - Add a tenant with client credentials
* [auth0 tenants list](auth0_tenants_list.md) - List your tenants
* [auth0 tenants open](auth0_tenants_open.md) - Open tenant settings page in the Auth0 Dashboard
* [auth0 tenants use](auth0_tenants_use.md) - Set the active tenant
Expand Down
41 changes: 0 additions & 41 deletions docs/auth0_tenants_add.md

This file was deleted.

4 changes: 2 additions & 2 deletions internal/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ func (c *cli) prepareTenant(ctx context.Context) (Tenant, error) {
}

if t.AccessToken == "" || (scopesChanged(t) && t.authenticatedWithDeviceCodeFlow()) {
return RunLogin(ctx, c, true)
return RunLoginAsUser(ctx, c, true)
}

if !t.hasExpiredToken() {
Expand All @@ -218,7 +218,7 @@ func (c *cli) prepareTenant(ctx context.Context) (Tenant, error) {
if err := t.regenerateAccessToken(ctx, c); err != nil {
// Ask and guide the user through the login process.
c.renderer.Errorf("failed to renew access token, %s", err)
return RunLogin(ctx, c, true)
return RunLoginAsUser(ctx, c, true)
}

if err := c.addTenant(t); err != nil {
Expand Down
109 changes: 104 additions & 5 deletions internal/cli/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,63 @@ import (
"github.com/auth0/auth0-cli/internal/prompt"
)

var (
loginTenantDomain = Flag{
Name: "Tenant Domain",
LongForm: "domain",
Help: "Tenant domain of the application when authenticating via client credentials.",
IsRequired: false,
AlwaysPrompt: false,
}

loginClientID = Flag{
Name: "Client ID",
LongForm: "client-id",
Help: "Client ID of the application when authenticating via client credentials.",
IsRequired: false,
AlwaysPrompt: false,
}

loginClientSecret = Flag{
Name: "Client Secret",
LongForm: "client-secret",
Help: "Client secret of the application when authenticating via client credentials.",
IsRequired: false,
AlwaysPrompt: false,
}
)

type LoginInputs struct {
Domain string
ClientID string
ClientSecret string
}

func (i *LoginInputs) shouldLoginAsMachine() bool {
return i.ClientID != "" || i.ClientSecret != "" || i.Domain != ""
}

func loginCmd(cli *cli) *cobra.Command {
var inputs LoginInputs

cmd := &cobra.Command{
Use: "login",
Args: cobra.NoArgs,
Short: "Authenticate the Auth0 CLI",
Long: "Sign in to your Auth0 account and authorize the CLI to access the Management API.",
Long: "Authenticates the Auth0 CLI either as a user using personal credentials or as a machine using client credentials.",
Example: `auth0 login
auth0 login --domain <tenant-domain> --client-id <client-id> --client-secret <client-secret>`,
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
if _, err := RunLogin(ctx, cli, false); err != nil {
return err

if inputs.shouldLoginAsMachine() {
if err := RunLoginAsMachine(ctx, inputs, cli, cmd); err != nil {
return err
}
} else {
if _, err := RunLoginAsUser(ctx, cli, false); err != nil {
return err
}
}

cli.tracker.TrackCommandRun(cmd, cli.config.InstallID)
Expand All @@ -30,20 +77,26 @@ func loginCmd(cli *cli) *cobra.Command {
},
}

loginTenantDomain.RegisterString(cmd, &inputs.Domain, "")
loginClientID.RegisterString(cmd, &inputs.ClientID, "")
loginClientSecret.RegisterString(cmd, &inputs.ClientSecret, "")
cmd.MarkFlagsRequiredTogether("client-id", "client-secret", "domain")

cmd.SetHelpFunc(func(cmd *cobra.Command, args []string) {
_ = cmd.Flags().MarkHidden("tenant")
_ = cmd.Flags().MarkHidden("json")
_ = cmd.Flags().MarkHidden("no-input")
cmd.Parent().HelpFunc()(cmd, args)
})

return cmd
}

// RunLogin runs the login flow guiding the user through the process
// RunLoginAsUser runs the login flow guiding the user through the process
// by showing the login instructions, opening the browser.
// Use `expired` to run the login from other commands setup:
// this will only affect the messages.
func RunLogin(ctx context.Context, cli *cli, expired bool) (Tenant, error) {
func RunLoginAsUser(ctx context.Context, cli *cli, expired bool) (Tenant, error) {
message := fmt.Sprintf(
"%s\n\n%s\n\n",
"✪ Welcome to the Auth0 CLI 🎊",
Expand Down Expand Up @@ -141,3 +194,49 @@ func RunLogin(ctx context.Context, cli *cli, expired bool) (Tenant, error) {

return tenant, nil
}

// RunLoginAsMachine facilitates the authentication process using client credentials (client ID, client secret)
func RunLoginAsMachine(ctx context.Context, inputs LoginInputs, cli *cli, cmd *cobra.Command) error {
if err := loginTenantDomain.Ask(cmd, &inputs.Domain, nil); err != nil {
return err
}

if err := loginClientID.Ask(cmd, &inputs.ClientID, nil); err != nil {
return err
}

if err := loginClientSecret.AskPassword(cmd, &inputs.ClientSecret, nil); err != nil {
return err
}

token, err := auth.GetAccessTokenFromClientCreds(auth.ClientCredentials{
ClientID: inputs.ClientID,
ClientSecret: inputs.ClientSecret,
Domain: inputs.Domain,
})
if err != nil {
return err
}

t := Tenant{
Domain: inputs.Domain,
AccessToken: token.AccessToken,
ExpiresAt: token.ExpiresAt,
ClientID: inputs.ClientID,
ClientSecret: inputs.ClientSecret,
}

if err := cli.addTenant(t); err != nil {
return fmt.Errorf("unexpected error when attempting to save tenant data: %w", err)
}

cli.renderer.Newline()
cli.renderer.Infof("Successfully logged in.")
cli.renderer.Infof("Tenant: %s", inputs.Domain)

if err := checkInstallID(cli); err != nil {
return fmt.Errorf("failed to update the config: %w", err)
}

return nil
}
100 changes: 5 additions & 95 deletions internal/cli/tenants.go
Original file line number Diff line number Diff line change
@@ -1,38 +1,13 @@
package cli

import (
"errors"
"fmt"

"github.com/spf13/cobra"

"github.com/auth0/auth0-cli/internal/auth"
"github.com/auth0/auth0-cli/internal/prompt"
)

var (
tenantDomain = Argument{
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 {
cmd := &cobra.Command{
Use: "tenants",
Expand All @@ -44,7 +19,6 @@ func tenantsCmd(cli *cli) *cobra.Command {
cmd.AddCommand(useTenantCmd(cli))
cmd.AddCommand(listTenantCmd(cli))
cmd.AddCommand(openTenantCmd(cli))
cmd.AddCommand(addTenantCmd(cli))
return cmd
}

Expand Down Expand Up @@ -129,6 +103,11 @@ func openTenantCmd(cli *cli) *cobra.Command {
Domain string
}

var tenantDomain = Argument{
Name: "Tenant",
Help: "Tenant to select",
}

cmd := &cobra.Command{
Use: "open",
Args: cobra.MaximumNArgs(1),
Expand Down Expand Up @@ -162,75 +141,6 @@ 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 <tenant> --client-id <id> --client-secret <secret>",
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
err := tenantDomain.Pick(cmd, &inputs.Domain, cli.tenantPickerOptions)
if err != nil {
if !errors.Is(err, errUnauthenticated) {
return err
}

if err := tenantDomain.Ask(cmd, &inputs.Domain); 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
}

token, err := auth.GetAccessTokenFromClientCreds(auth.ClientCredentials{
ClientID: inputs.ClientID,
ClientSecret: inputs.ClientSecret,
Domain: inputs.Domain,
})
if err != nil {
return err
}

t := Tenant{
Domain: inputs.Domain,
AccessToken: token.AccessToken,
ExpiresAt: token.ExpiresAt,
ClientID: inputs.ClientID,
ClientSecret: inputs.ClientSecret,
}

if err := cli.addTenant(t); err != nil {
return fmt.Errorf("unexpected error when attempting to save tenant data: %w", err)
}

cli.renderer.Infof("Tenant added successfully: %s", t.Domain)
return nil
},
}

tenantClientID.RegisterString(cmd, &inputs.ClientID, "")
tenantClientSecret.RegisterString(cmd, &inputs.ClientSecret, "")

return cmd
}

func (c *cli) tenantPickerOptions() (pickerOptions, error) {
tens, err := c.listTenants()
if err != nil {
Expand Down
Loading