diff --git a/internal/cli/universal_login_customize.go b/internal/cli/universal_login_customize.go index 29599d74e..d631d1b52 100644 --- a/internal/cli/universal_login_customize.go +++ b/internal/cli/universal_login_customize.go @@ -30,17 +30,19 @@ const ( type ( universalLoginBrandingData struct { - Settings *management.Branding `json:"settings"` - Template *management.BrandingUniversalLogin `json:"template"` - Theme *management.BrandingTheme `json:"theme"` - Tenant *tenantData `json:"tenant"` - Prompts []*promptData `json:"prompts"` + Applications []*applicationData `json:"applications"` + Prompts []*promptData `json:"prompts"` + Settings *management.Branding `json:"settings"` + Template *management.BrandingUniversalLogin `json:"template"` + Theme *management.BrandingTheme `json:"theme"` + Tenant *tenantData `json:"tenant"` } - tenantData struct { - FriendlyName string `json:"friendly_name"` - EnabledLocales []string `json:"enabled_locales"` - Domain string `json:"domain"` + applicationData struct { + ID string `json:"id"` + Name string `json:"name"` + LogoURL string `json:"logo_url"` + Metadata map[string]interface{} `json:"metadata"` } promptData struct { @@ -49,6 +51,12 @@ type ( CustomText map[string]interface{} `json:"custom_text,omitempty"` } + tenantData struct { + FriendlyName string `json:"friendly_name"` + EnabledLocales []string `json:"enabled_locales"` + Domain string `json:"domain"` + } + errorData struct { Error string `json:"error"` } @@ -213,14 +221,21 @@ func fetchUniversalLoginBrandingData( return err }) + var applications []*applicationData + group.Go(func() (err error) { + applications, err = fetchAllApplications(ctx, api) + return err + }) + if err := group.Wait(); err != nil { return nil, err } return &universalLoginBrandingData{ - Settings: brandingSettings, - Template: currentTemplate, - Theme: currentTheme, + Applications: applications, + Settings: brandingSettings, + Template: currentTemplate, + Theme: currentTheme, Tenant: &tenantData{ FriendlyName: tenant.GetFriendlyName(), EnabledLocales: tenant.GetEnabledLocales(), @@ -340,6 +355,39 @@ func fetchPromptCustomTextWithDefaults( }, nil } +func fetchAllApplications(ctx context.Context, api *auth0.API) ([]*applicationData, error) { + var applications []*applicationData + var page int + for { + clientList, err := api.Client.List( + ctx, + management.Page(page), + management.PerPage(100), + management.IncludeFields("client_id", "name", "logo_uri", "client_metadata"), + ) + if err != nil { + return nil, err + } + + for _, client := range clientList.Clients { + applications = append(applications, &applicationData{ + ID: client.GetClientID(), + Name: client.GetName(), + LogoURL: client.GetLogoURI(), + Metadata: client.GetClientMetadata(), + }) + } + + if !clientList.HasNext() { + break + } + + page++ + } + + return applications, nil +} + func startWebSocketServer( ctx context.Context, api *auth0.API, diff --git a/internal/cli/universal_login_customize_test.go b/internal/cli/universal_login_customize_test.go index a10224dc7..e21c5dd6a 100644 --- a/internal/cli/universal_login_customize_test.go +++ b/internal/cli/universal_login_customize_test.go @@ -168,7 +168,23 @@ func TestFetchUniversalLoginBrandingData(t *testing.T) { nil, ) + mockClientAPI := mock.NewMockClientAPI(ctrl) + mockClientAPI. + EXPECT(). + List(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). + Return(&management.ClientList{ + Clients: []*management.Client{ + { + ClientID: auth0.String("1"), + Name: auth0.String("My App"), + LogoURI: auth0.String("https://my-app.example.com/image.png"), + ClientMetadata: &map[string]interface{}{"meta": "meta"}, + }, + }, + }, nil) + mockAPI := &auth0.API{ + Client: mockClientAPI, Branding: mockBrandingAPI, BrandingTheme: mockBrandingThemeAPI, Prompt: mockPromptAPI, @@ -178,6 +194,14 @@ func TestFetchUniversalLoginBrandingData(t *testing.T) { return mockAPI }, expectedData: &universalLoginBrandingData{ + Applications: []*applicationData{ + { + ID: "1", + Name: "My App", + LogoURL: "https://my-app.example.com/image.png", + Metadata: map[string]interface{}{"meta": "meta"}, + }, + }, Settings: &management.Branding{ Colors: &management.BrandingColors{ Primary: auth0.String("#334455"), @@ -292,7 +316,23 @@ func TestFetchUniversalLoginBrandingData(t *testing.T) { nil, ) + mockClientAPI := mock.NewMockClientAPI(ctrl) + mockClientAPI. + EXPECT(). + List(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). + Return(&management.ClientList{ + Clients: []*management.Client{ + { + ClientID: auth0.String("1"), + Name: auth0.String("My App"), + LogoURI: auth0.String("https://my-app.example.com/image.png"), + ClientMetadata: &map[string]interface{}{"meta": "meta"}, + }, + }, + }, nil) + mockAPI := &auth0.API{ + Client: mockClientAPI, Branding: mockBrandingAPI, BrandingTheme: mockBrandingThemeAPI, Prompt: mockPromptAPI, @@ -302,6 +342,14 @@ func TestFetchUniversalLoginBrandingData(t *testing.T) { return mockAPI }, expectedData: &universalLoginBrandingData{ + Applications: []*applicationData{ + { + ID: "1", + Name: "My App", + LogoURL: "https://my-app.example.com/image.png", + Metadata: map[string]interface{}{"meta": "meta"}, + }, + }, Settings: &management.Branding{ Colors: &management.BrandingColors{ Primary: auth0.String(defaultPrimaryColor), @@ -420,7 +468,23 @@ func TestFetchUniversalLoginBrandingData(t *testing.T) { nil, ) + mockClientAPI := mock.NewMockClientAPI(ctrl) + mockClientAPI. + EXPECT(). + List(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). + Return(&management.ClientList{ + Clients: []*management.Client{ + { + ClientID: auth0.String("1"), + Name: auth0.String("My App"), + LogoURI: auth0.String("https://my-app.example.com/image.png"), + ClientMetadata: &map[string]interface{}{"meta": "meta"}, + }, + }, + }, nil) + mockAPI := &auth0.API{ + Client: mockClientAPI, Branding: mockBrandingAPI, BrandingTheme: mockBrandingThemeAPI, Prompt: mockPromptAPI, @@ -430,6 +494,14 @@ func TestFetchUniversalLoginBrandingData(t *testing.T) { return mockAPI }, expectedData: &universalLoginBrandingData{ + Applications: []*applicationData{ + { + ID: "1", + Name: "My App", + LogoURL: "https://my-app.example.com/image.png", + Metadata: map[string]interface{}{"meta": "meta"}, + }, + }, Settings: &management.Branding{ Colors: &management.BrandingColors{ Primary: auth0.String("#334455"), @@ -551,7 +623,23 @@ func TestFetchUniversalLoginBrandingData(t *testing.T) { nil, ) + mockClientAPI := mock.NewMockClientAPI(ctrl) + mockClientAPI. + EXPECT(). + List(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). + Return(&management.ClientList{ + Clients: []*management.Client{ + { + ClientID: auth0.String("1"), + Name: auth0.String("My App"), + LogoURI: auth0.String("https://my-app.example.com/image.png"), + ClientMetadata: &map[string]interface{}{"meta": "meta"}, + }, + }, + }, nil) + mockAPI := &auth0.API{ + Client: mockClientAPI, Branding: mockBrandingAPI, BrandingTheme: mockBrandingThemeAPI, Prompt: mockPromptAPI, @@ -561,6 +649,14 @@ func TestFetchUniversalLoginBrandingData(t *testing.T) { return mockAPI }, expectedData: &universalLoginBrandingData{ + Applications: []*applicationData{ + { + ID: "1", + Name: "My App", + LogoURL: "https://my-app.example.com/image.png", + Metadata: map[string]interface{}{"meta": "meta"}, + }, + }, Settings: &management.Branding{ Colors: &management.BrandingColors{ Primary: auth0.String("#334455"), @@ -740,7 +836,23 @@ func TestFetchUniversalLoginBrandingData(t *testing.T) { Read(gomock.Any()). Return(nil, fmt.Errorf("failed to fetch tenant data")) + mockClientAPI := mock.NewMockClientAPI(ctrl) + mockClientAPI. + EXPECT(). + List(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). + Return(&management.ClientList{ + Clients: []*management.Client{ + { + ClientID: auth0.String("1"), + Name: auth0.String("My App"), + LogoURI: auth0.String("https://my-app.example.com/image.png"), + ClientMetadata: &map[string]interface{}{"meta": "meta"}, + }, + }, + }, nil) + mockAPI := &auth0.API{ + Client: mockClientAPI, Branding: mockBrandingAPI, BrandingTheme: mockBrandingThemeAPI, Prompt: mockPromptAPI, @@ -803,7 +915,23 @@ func TestFetchUniversalLoginBrandingData(t *testing.T) { CustomText(gomock.Any(), "login", "en"). Return(nil, fmt.Errorf("failed to fetch custom text")) + mockClientAPI := mock.NewMockClientAPI(ctrl) + mockClientAPI. + EXPECT(). + List(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). + Return(&management.ClientList{ + Clients: []*management.Client{ + { + ClientID: auth0.String("1"), + Name: auth0.String("My App"), + LogoURI: auth0.String("https://my-app.example.com/image.png"), + ClientMetadata: &map[string]interface{}{"meta": "meta"}, + }, + }, + }, nil) + mockAPI := &auth0.API{ + Client: mockClientAPI, Branding: mockBrandingAPI, BrandingTheme: mockBrandingThemeAPI, Prompt: mockPromptAPI,