diff --git a/docs/auth0_domains.md b/docs/auth0_domains.md index 833aafd36..b29517bc9 100644 --- a/docs/auth0_domains.md +++ b/docs/auth0_domains.md @@ -10,6 +10,7 @@ With a custom domain, your users feel confident that they are providing their cr ## Commands - [auth0 domains create](auth0_domains_create.md) - Create a custom domain +- [auth0 domains default](auth0_domains_default.md) - Manage the default custom domain - [auth0 domains delete](auth0_domains_delete.md) - Delete a custom domain - [auth0 domains list](auth0_domains_list.md) - List your custom domains - [auth0 domains show](auth0_domains_show.md) - Show a custom domain diff --git a/docs/auth0_domains_create.md b/docs/auth0_domains_create.md index d787079cb..410937e51 100644 --- a/docs/auth0_domains_create.md +++ b/docs/auth0_domains_create.md @@ -57,6 +57,7 @@ auth0 domains create [flags] ## Related Commands - [auth0 domains create](auth0_domains_create.md) - Create a custom domain +- [auth0 domains default](auth0_domains_default.md) - Manage the default custom domain - [auth0 domains delete](auth0_domains_delete.md) - Delete a custom domain - [auth0 domains list](auth0_domains_list.md) - List your custom domains - [auth0 domains show](auth0_domains_show.md) - Show a custom domain diff --git a/docs/auth0_domains_default.md b/docs/auth0_domains_default.md new file mode 100644 index 000000000..0f1b369da --- /dev/null +++ b/docs/auth0_domains_default.md @@ -0,0 +1,14 @@ +--- +layout: default +has_toc: false +has_children: true +--- +# auth0 domains default + +Manage the default custom domain for the tenant. Use sub-commands to show or set the default domain. + +## Commands + +- [auth0 domains default set](auth0_domains_default_set.md) - Set the default custom domain +- [auth0 domains default show](auth0_domains_default_show.md) - Show the default custom domain + diff --git a/docs/auth0_domains_default_set.md b/docs/auth0_domains_default_set.md new file mode 100644 index 000000000..6f32cda74 --- /dev/null +++ b/docs/auth0_domains_default_set.md @@ -0,0 +1,53 @@ +--- +layout: default +parent: auth0 domains default +has_toc: false +--- +# auth0 domains default set + +Set the default custom domain for the tenant. + +To set interactively, use `auth0 domains default set` with no arguments. + +To set non-interactively, supply the domain name as an argument or through the flag. + +## Usage +``` +auth0 domains default set [flags] +``` + +## Examples + +``` + auth0 domains default set + auth0 domains default set + auth0 domains default set --domain + auth0 domains default set --domain --json +``` + + +## Flags + +``` + -d, --domain string Domain name. + --json Output in json format. + --json-compact Output in compact json format. +``` + + +## Inherited Flags + +``` + --debug Enable debug mode. + --no-color Disable colors. + --no-input Disable interactivity. + --tenant string Specific tenant to use. +``` + + +## Related Commands + +- [auth0 domains default set](auth0_domains_default_set.md) - Set the default custom domain +- [auth0 domains default show](auth0_domains_default_show.md) - Show the default custom domain + + diff --git a/docs/auth0_domains_default_show.md b/docs/auth0_domains_default_show.md new file mode 100644 index 000000000..adcc61591 --- /dev/null +++ b/docs/auth0_domains_default_show.md @@ -0,0 +1,47 @@ +--- +layout: default +parent: auth0 domains default +has_toc: false +--- +# auth0 domains default show + +Display the default custom domain configuration for the tenant. + +## Usage +``` +auth0 domains default show [flags] +``` + +## Examples + +``` + auth0 domains default show + auth0 domains default show --json + auth0 domains default show --json-compact +``` + + +## Flags + +``` + --json Output in json format. + --json-compact Output in compact json format. +``` + + +## Inherited Flags + +``` + --debug Enable debug mode. + --no-color Disable colors. + --no-input Disable interactivity. + --tenant string Specific tenant to use. +``` + + +## Related Commands + +- [auth0 domains default set](auth0_domains_default_set.md) - Set the default custom domain +- [auth0 domains default show](auth0_domains_default_show.md) - Show the default custom domain + + diff --git a/docs/auth0_domains_delete.md b/docs/auth0_domains_delete.md index 6fd486dc2..a31bd2842 100644 --- a/docs/auth0_domains_delete.md +++ b/docs/auth0_domains_delete.md @@ -48,6 +48,7 @@ auth0 domains delete [flags] ## Related Commands - [auth0 domains create](auth0_domains_create.md) - Create a custom domain +- [auth0 domains default](auth0_domains_default.md) - Manage the default custom domain - [auth0 domains delete](auth0_domains_delete.md) - Delete a custom domain - [auth0 domains list](auth0_domains_list.md) - List your custom domains - [auth0 domains show](auth0_domains_show.md) - Show a custom domain diff --git a/docs/auth0_domains_list.md b/docs/auth0_domains_list.md index a044dd42d..ef74aaa4e 100644 --- a/docs/auth0_domains_list.md +++ b/docs/auth0_domains_list.md @@ -48,6 +48,7 @@ auth0 domains list [flags] ## Related Commands - [auth0 domains create](auth0_domains_create.md) - Create a custom domain +- [auth0 domains default](auth0_domains_default.md) - Manage the default custom domain - [auth0 domains delete](auth0_domains_delete.md) - Delete a custom domain - [auth0 domains list](auth0_domains_list.md) - List your custom domains - [auth0 domains show](auth0_domains_show.md) - Show a custom domain diff --git a/docs/auth0_domains_show.md b/docs/auth0_domains_show.md index 9ebf7de0a..c41ff89ef 100644 --- a/docs/auth0_domains_show.md +++ b/docs/auth0_domains_show.md @@ -43,6 +43,7 @@ auth0 domains show [flags] ## Related Commands - [auth0 domains create](auth0_domains_create.md) - Create a custom domain +- [auth0 domains default](auth0_domains_default.md) - Manage the default custom domain - [auth0 domains delete](auth0_domains_delete.md) - Delete a custom domain - [auth0 domains list](auth0_domains_list.md) - List your custom domains - [auth0 domains show](auth0_domains_show.md) - Show a custom domain diff --git a/docs/auth0_domains_update.md b/docs/auth0_domains_update.md index 5b653e48f..9ac620fa9 100644 --- a/docs/auth0_domains_update.md +++ b/docs/auth0_domains_update.md @@ -52,6 +52,7 @@ auth0 domains update [flags] ## Related Commands - [auth0 domains create](auth0_domains_create.md) - Create a custom domain +- [auth0 domains default](auth0_domains_default.md) - Manage the default custom domain - [auth0 domains delete](auth0_domains_delete.md) - Delete a custom domain - [auth0 domains list](auth0_domains_list.md) - List your custom domains - [auth0 domains show](auth0_domains_show.md) - Show a custom domain diff --git a/docs/auth0_domains_verify.md b/docs/auth0_domains_verify.md index 5a1600867..9d72f9586 100644 --- a/docs/auth0_domains_verify.md +++ b/docs/auth0_domains_verify.md @@ -45,6 +45,7 @@ auth0 domains verify [flags] ## Related Commands - [auth0 domains create](auth0_domains_create.md) - Create a custom domain +- [auth0 domains default](auth0_domains_default.md) - Manage the default custom domain - [auth0 domains delete](auth0_domains_delete.md) - Delete a custom domain - [auth0 domains list](auth0_domains_list.md) - List your custom domains - [auth0 domains show](auth0_domains_show.md) - Show a custom domain diff --git a/go.mod b/go.mod index f445941c8..12e758264 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/AlecAivazis/survey/v2 v2.3.7 github.com/PuerkitoBio/rehttp v1.4.0 github.com/atotto/clipboard v0.1.4 - github.com/auth0/go-auth0 v1.36.0 + github.com/auth0/go-auth0 v1.36.1-0.20260317125325-3fac89c7c7d6 github.com/auth0/go-auth0/v2 v2.7.0 github.com/briandowns/spinner v1.23.2 github.com/charmbracelet/glamour v1.0.0 diff --git a/go.sum b/go.sum index 561534c84..e02992c7f 100644 --- a/go.sum +++ b/go.sum @@ -22,8 +22,8 @@ github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= -github.com/auth0/go-auth0 v1.36.0 h1:B3gxl26i4HLKoUmpEF4KrtsEA0Tx0Quxci+9f8/Lvfg= -github.com/auth0/go-auth0 v1.36.0/go.mod h1:32sQB1uAn+99fJo6N819EniKq8h785p0ag0lMWhiTaE= +github.com/auth0/go-auth0 v1.36.1-0.20260317125325-3fac89c7c7d6 h1:U2c6oyHdYeVBRkiWZyQ03jB9OZZCdKBdABRNc409z4Q= +github.com/auth0/go-auth0 v1.36.1-0.20260317125325-3fac89c7c7d6/go.mod h1:32sQB1uAn+99fJo6N819EniKq8h785p0ag0lMWhiTaE= github.com/auth0/go-auth0/v2 v2.7.0 h1:uwY9yWGbtuU+M5z8GtK+smgUleu7+gfCA8hIpbxxd+Y= github.com/auth0/go-auth0/v2 v2.7.0/go.mod h1:Q/Y3VZVoI3sw87VyTPhx2TQL6Sq4Q/iCP67rW2gcn+M= github.com/aybabtme/iocontrol v0.0.0-20150809002002-ad15bcfc95a0 h1:0NmehRCgyk5rljDQLKUO+cRJCnduDyn11+zGZIc9Z48= diff --git a/internal/auth0/custom_domain.go b/internal/auth0/custom_domain.go index e652b5015..26f6ecd54 100644 --- a/internal/auth0/custom_domain.go +++ b/internal/auth0/custom_domain.go @@ -29,4 +29,10 @@ type CustomDomainAPI interface { // ListWithPagination lists all custom domains with support for pagination. ListWithPagination(ctx context.Context, opts ...management.RequestOption) (c *management.CustomDomainList, err error) + + // ReadDefault retrieves the default domain configuration for the tenant. + ReadDefault(ctx context.Context, opts ...management.RequestOption) (c *management.CustomDomainDefault, err error) + + // UpdateDefault updates the default domain for the tenant. + UpdateDefault(ctx context.Context, c *management.CustomDomainDefault, opts ...management.RequestOption) error } diff --git a/internal/auth0/mock/custom_domain_mock.go b/internal/auth0/mock/custom_domain_mock.go index 6dac39c5b..09229144b 100644 --- a/internal/auth0/mock/custom_domain_mock.go +++ b/internal/auth0/mock/custom_domain_mock.go @@ -171,3 +171,43 @@ func (mr *MockCustomDomainAPIMockRecorder) Verify(ctx, id interface{}, opts ...i varargs := append([]interface{}{ctx, id}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Verify", reflect.TypeOf((*MockCustomDomainAPI)(nil).Verify), varargs...) } + +// ReadDefault mocks base method. +func (m *MockCustomDomainAPI) ReadDefault(ctx context.Context, opts ...management.RequestOption) (*management.CustomDomainDefault, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ReadDefault", varargs...) + ret0, _ := ret[0].(*management.CustomDomainDefault) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ReadDefault indicates an expected call of ReadDefault. +func (mr *MockCustomDomainAPIMockRecorder) ReadDefault(ctx interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadDefault", reflect.TypeOf((*MockCustomDomainAPI)(nil).ReadDefault), varargs...) +} + +// UpdateDefault mocks base method. +func (m *MockCustomDomainAPI) UpdateDefault(ctx context.Context, c *management.CustomDomainDefault, opts ...management.RequestOption) error { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, c} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "UpdateDefault", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// UpdateDefault indicates an expected call of UpdateDefault. +func (mr *MockCustomDomainAPIMockRecorder) UpdateDefault(ctx, c interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, c}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDefault", reflect.TypeOf((*MockCustomDomainAPI)(nil).UpdateDefault), varargs...) +} + diff --git a/internal/cli/custom_domains.go b/internal/cli/custom_domains.go index 840063de8..27d376eb9 100644 --- a/internal/cli/custom_domains.go +++ b/internal/cli/custom_domains.go @@ -99,6 +99,7 @@ func customDomainsCmd(cli *cli) *cobra.Command { cmd.AddCommand(updateCustomDomainCmd(cli)) cmd.AddCommand(deleteCustomDomainCmd(cli)) cmd.AddCommand(verifyCustomDomainCmd(cli)) + cmd.AddCommand(defaultCustomDomainCmd(cli)) return cmd } @@ -517,6 +518,101 @@ func apiProvisioningTypeFor(v string) *string { } } +func defaultCustomDomainCmd(cli *cli) *cobra.Command { + cmd := &cobra.Command{ + Use: "default", + Short: "Manage the default custom domain", + Long: "Manage the default custom domain for the tenant. Use sub-commands to show or set the default domain.", + } + + cmd.SetUsageTemplate(resourceUsageTemplate()) + cmd.AddCommand(showDefaultCustomDomainCmd(cli)) + cmd.AddCommand(setDefaultCustomDomainCmd(cli)) + + return cmd +} + +func showDefaultCustomDomainCmd(cli *cli) *cobra.Command { + cmd := &cobra.Command{ + Use: "show", + Args: cobra.NoArgs, + Short: "Show the default custom domain", + Long: "Display the default custom domain configuration for the tenant.", + Example: ` auth0 domains default show + auth0 domains default show --json + auth0 domains default show --json-compact`, + RunE: func(cmd *cobra.Command, args []string) error { + var defaultDomain *management.CustomDomainDefault + + if err := ansi.Waiting(func() (err error) { + defaultDomain, err = cli.api.CustomDomain.ReadDefault(cmd.Context()) + return err + }); err != nil { + return fmt.Errorf("failed to read default custom domain: %w", err) + } + + cli.renderer.CustomDomainDefaultShow(defaultDomain) + + return nil + }, + } + + cmd.Flags().BoolVar(&cli.json, "json", false, "Output in json format.") + cmd.Flags().BoolVar(&cli.jsonCompact, "json-compact", false, "Output in compact json format.") + + return cmd +} + +func setDefaultCustomDomainCmd(cli *cli) *cobra.Command { + var inputs struct { + Domain string + } + + cmd := &cobra.Command{ + Use: "set", + Args: cobra.MaximumNArgs(1), + Short: "Set the default custom domain", + Long: "Set the default custom domain for the tenant.\n\n" + + "To set interactively, use `auth0 domains default set` with no arguments.\n\n" + + "To set non-interactively, supply the domain name as an argument or through the flag.", + Example: ` auth0 domains default set + auth0 domains default set + auth0 domains default set --domain + auth0 domains default set --domain --json`, + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) > 0 { + inputs.Domain = args[0] + } + + if inputs.Domain == "" { + if err := customDomainDomain.Pick(cmd, &inputs.Domain, cli.customDomainNamePickerOptions); err != nil { + return err + } + } + + defaultDomain := &management.CustomDomainDefault{ + Domain: &inputs.Domain, + } + + if err := ansi.Waiting(func() error { + return cli.api.CustomDomain.UpdateDefault(cmd.Context(), defaultDomain) + }); err != nil { + return fmt.Errorf("failed to set default custom domain to %q: %w", inputs.Domain, err) + } + + cli.renderer.CustomDomainDefaultUpdate(defaultDomain) + + return nil + }, + } + + customDomainDomain.RegisterString(cmd, &inputs.Domain, "") + cmd.Flags().BoolVar(&cli.json, "json", false, "Output in json format.") + cmd.Flags().BoolVar(&cli.jsonCompact, "json-compact", false, "Output in compact json format.") + + return cmd +} + func apiVerificationMethodFor(v string) *string { switch v { case "txt": @@ -565,3 +661,32 @@ func (c *cli) customDomainsPickerOptions(ctx context.Context) (pickerOptions, er return opts, nil } + +func (c *cli) customDomainNamePickerOptions(ctx context.Context) (pickerOptions, error) { + var opts pickerOptions + + domains, err := c.api.CustomDomain.List(ctx) + if err != nil { + var errStatus management.Error + errors.As(err, &errStatus) + // 403 is a valid response for free tenants that don't have + // custom domains enabled. + if errStatus != nil && errStatus.Status() == 403 { + return nil, errNoCustomDomains + } + + return nil, fmt.Errorf("failed to list custom domains: %w", err) + } + + for _, d := range domains { + domain := d.GetDomain() + label := fmt.Sprintf("%s %s", domain, ansi.Faint("("+d.GetID()+")")) + opts = append(opts, pickerOption{value: domain, label: label}) + } + + if len(opts) == 0 { + return nil, errNoCustomDomains + } + + return opts, nil +} diff --git a/internal/display/custom_domain.go b/internal/display/custom_domain.go index f4fed5791..0c872fe87 100644 --- a/internal/display/custom_domain.go +++ b/internal/display/custom_domain.go @@ -22,6 +22,7 @@ type customDomainView struct { CertificateErrorMsg string CertificateAuthority string CertificateRenewsBefore string + IsDefault string raw interface{} } @@ -85,6 +86,9 @@ func (v *customDomainView) KeyValues() [][]string { if v.CertificateRenewsBefore != "" { keyValues = append(keyValues, []string{ansi.Yellow(ansi.Bold("CERTIFICATE RENEWS BEFORE")), ansi.Yellow(ansi.Bold(v.CertificateRenewsBefore))}) } + if v.IsDefault != "" { + keyValues = append(keyValues, []string{ansi.Yellow(ansi.Bold("IS DEFAULT")), ansi.Yellow(ansi.Bold(v.IsDefault))}) + } return keyValues } @@ -125,6 +129,16 @@ func (r *Renderer) CustomDomainUpdate(customDomain *management.CustomDomain) { r.Result(makeCustomDomainView(customDomain)) } +func (r *Renderer) CustomDomainDefaultShow(defaultDomain *management.CustomDomainDefault) { + r.Heading("default custom domain") + r.Result(makeCustomDomainDefaultView(defaultDomain)) +} + +func (r *Renderer) CustomDomainDefaultUpdate(defaultDomain *management.CustomDomainDefault) { + r.Heading("default custom domain updated") + r.Result(makeCustomDomainDefaultView(defaultDomain)) +} + func makeCustomDomainView(customDomain *management.CustomDomain) *customDomainView { metadata, err := toJSONString(customDomain.GetDomainMetadata()) if err != nil { @@ -143,6 +157,7 @@ func makeCustomDomainView(customDomain *management.CustomDomain) *customDomainVi CertificateStatus: customDomain.GetCertificate().GetStatus(), CertificateAuthority: customDomain.GetCertificate().GetCertificateAuthority(), CertificateRenewsBefore: customDomain.GetCertificate().GetRenewsBefore(), + IsDefault: boolean(customDomain.GetIsDefault()), raw: customDomain, } @@ -174,3 +189,42 @@ func customDomainStatusColor(v string) string { return v } } + +type customDomainDefaultView struct { + Domain string + raw interface{} +} + +func (v *customDomainDefaultView) AsTableHeader() []string { + return []string{"Domain"} +} + +func (v *customDomainDefaultView) AsTableRow() []string { + return []string{v.Domain} +} + +func (v *customDomainDefaultView) KeyValues() [][]string { + var keyValues [][]string + + if v.Domain != "" { + keyValues = append(keyValues, []string{"DOMAIN", v.Domain}) + } + + return keyValues +} + +func (v *customDomainDefaultView) Object() interface{} { + return v.raw +} + +func makeCustomDomainDefaultView(defaultDomain *management.CustomDomainDefault) *customDomainDefaultView { + domain := "" + if defaultDomain.Domain != nil { + domain = *defaultDomain.Domain + } + + return &customDomainDefaultView{ + Domain: domain, + raw: defaultDomain, + } +}