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

feat: CLI Custom Prompts [HACKATHON] #1031

Merged
merged 17 commits into from
Aug 12, 2024
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
4 changes: 4 additions & 0 deletions internal/auth0/branding_prompt.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,8 @@ type PromptAPI interface {
//
// See: https://auth0.com/docs/api/management/v2#!/Prompts/put_custom_text_by_language
SetCustomText(ctx context.Context, p string, l string, b map[string]interface{}, opts ...management.RequestOption) (err error)

ReadPartials(ctx context.Context, prompt management.PromptType, opts ...management.RequestOption) (c *management.PromptPartials, err error)

UpdatePartials(ctx context.Context, c *management.PromptPartials, opts ...management.RequestOption) error
}
39 changes: 39 additions & 0 deletions internal/auth0/mock/branding_prompt_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9,672 changes: 0 additions & 9,672 deletions internal/cli/data/universal-login/assets/index-89540052.js

This file was deleted.

23,194 changes: 23,194 additions & 0 deletions internal/cli/data/universal-login/assets/index-da4e01b2.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion internal/cli/data/universal-login/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<link rel="icon" type="image/png" href="https://cdn.auth0.com/quantum-assets/dist/1.0.3/favicons/auth0-favicon-onlight.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Auth0 | Branding Customization</title>
<script type="module" crossorigin src="/assets/index-89540052.js"></script>
<script type="module" crossorigin src="/assets/index-da4e01b2.js"></script>
<link rel="stylesheet" href="/assets/index-ba9d88e4.css">
</head>
<body>
Expand Down
137 changes: 136 additions & 1 deletion internal/cli/universal_login_customize.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"net"
"net/http"
"net/url"
"strings"
"time"

"github.com/auth0/go-auth0/management"
Expand All @@ -28,6 +29,8 @@ const (
fetchBrandingMessageType = "FETCH_BRANDING"
fetchPromptMessageType = "FETCH_PROMPT"
saveBrandingMessageType = "SAVE_BRANDING"
fetchPartialMessageType = "FETCH_PARTIAL"
fetchPartialFeatureFlag = "FETCH_PARTIALS_FEATURE_FLAG"
errorMessageType = "ERROR"
successMessageType = "SUCCESS"
)
Expand All @@ -37,10 +40,20 @@ var (
universalLoginPreviewAssets embed.FS
)

var allowedPromptsWithPartials = []management.PromptType{
management.PromptSignup,
management.PromptSignupID,
management.PromptSignupPassword,
management.PromptLogin,
management.PromptLoginID,
management.PromptLoginPassword,
}

type (
universalLoginBrandingData struct {
Applications []*applicationData `json:"applications"`
Prompts []*promptData `json:"prompts"`
Partials []*management.PromptPartials `json:"partials"`
Settings *management.Branding `json:"settings"`
Template *management.BrandingUniversalLogin `json:"template"`
Theme *management.BrandingTheme `json:"theme"`
Expand All @@ -60,6 +73,15 @@ type (
CustomText map[string]interface{} `json:"custom_text,omitempty"`
}

partialData struct {
InsertionPoint string `json:"insertion_point"`
PromptName string `json:"prompt_name"`
}

partialFlagData struct {
FeatureFlag bool `json:"feature_flag"`
}

tenantData struct {
FriendlyName string `json:"friendly_name"`
EnabledLocales []string `json:"enabled_locales"`
Expand Down Expand Up @@ -130,6 +152,10 @@ func (m *webSocketMessage) UnmarshalJSON(b []byte) error {
payload = &universalLoginBrandingData{}
case fetchPromptMessageType:
payload = &promptData{}
case fetchPartialMessageType:
payload = &partialData{}
case fetchPartialFeatureFlag:
payload = &partialFlagData{}
default:
payload = make(map[string]interface{})
}
Expand Down Expand Up @@ -377,6 +403,72 @@ func (h *webSocketHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if err := connection.WriteJSON(&successMsg); err != nil {
h.display.Errorf("Failed to send success message: %v", err)
}
case fetchPartialFeatureFlag:
partial := &partialData{
InsertionPoint: "form-content-start",
PromptName: "login",
}
_, err := fetchPartial(r.Context(), h.api, partial)
if err != nil && strings.Contains(err.Error(), "feature is not available for your plan") {
fetchPartialFlagMsg := webSocketMessage{
Type: fetchPartialFeatureFlag,
Payload: &partialFlagData{FeatureFlag: false},
}
if err = connection.WriteJSON(&fetchPartialFlagMsg); err != nil {
h.display.Errorf("Failed to send partial flag data message: %v", err)
continue
}
} else {
fetchPartialFlagMsg := webSocketMessage{
Type: fetchPartialFeatureFlag,
Payload: &partialFlagData{FeatureFlag: true},
}

if err = connection.WriteJSON(&fetchPartialFlagMsg); err != nil {
h.display.Errorf("Failed to send partial flag data message: %v", err)
continue
}
}

case fetchPartialMessageType:
partialToFetch, ok := message.Payload.(*partialData)

if !ok {
h.display.Errorf("Invalid payload type: %T", message.Payload)
continue
}

partialToSend, err := fetchPartial(r.Context(), h.api, partialToFetch)

if err != nil {
if strings.Contains(err.Error(), "feature is not available for your plan") {
partialToSend = &management.PromptPartials{}
} else {
h.display.Errorf("Failed to fetch partial for prompt: %v", err)
errorMsg := webSocketMessage{
Type: errorMessageType,
Payload: &errorData{
Error: err.Error(),
},
}

if err := connection.WriteJSON(&errorMsg); err != nil {
h.display.Errorf("Failed to send error message: %v", err)
}

continue
}
}

fetchPartialMsg := webSocketMessage{
Type: fetchPartialMessageType,
Payload: partialToSend,
}

if err = connection.WriteJSON(&fetchPartialMsg); err != nil {
h.display.Errorf("Failed to send prompt data message: %v", err)
continue
}
}
}
}
Expand Down Expand Up @@ -441,6 +533,12 @@ func fetchUniversalLoginBrandingData(
return err
})

var partials []*management.PromptPartials
group.Go(func() (err error) {
partials, err = fetchAllPartials(ctx, api)
return err
})

if err := group.Wait(); err != nil {
return nil, err
}
Expand All @@ -455,7 +553,8 @@ func fetchUniversalLoginBrandingData(
EnabledLocales: tenant.GetEnabledLocales(),
Domain: tenantDomain,
},
Prompts: []*promptData{prompt},
Prompts: []*promptData{prompt},
Partials: partials,
}, nil
}

Expand Down Expand Up @@ -603,6 +702,34 @@ func fetchAllApplications(ctx context.Context, api *auth0.API) ([]*applicationDa
return applications, nil
}

func fetchPartial(ctx context.Context, api *auth0.API, prompt *partialData) (*management.PromptPartials, error) {
partial, err := api.Prompt.ReadPartials(ctx, management.PromptType(prompt.PromptName))
if err != nil {
return nil, err
}

return partial, nil
}

func fetchAllPartials(ctx context.Context, api *auth0.API) ([]*management.PromptPartials, error) {
var partials []*management.PromptPartials

for _, prompt := range allowedPromptsWithPartials {
partial, err := api.Prompt.ReadPartials(ctx, prompt)

if err != nil {
if strings.Contains(err.Error(), "feature is not available for your plan") {
return []*management.PromptPartials{}, nil
}
return nil, err
}

partials = append(partials, partial)
}

return partials, nil
}

func saveUniversalLoginBrandingData(ctx context.Context, api *auth0.API, data *universalLoginBrandingData) error {
group, ctx := errgroup.WithContext(ctx)

Expand Down Expand Up @@ -643,5 +770,13 @@ func saveUniversalLoginBrandingData(ctx context.Context, api *auth0.API, data *u
})
}

for _, partial := range data.Partials {
partial := partial

group.Go(func() (err error) {
return api.Prompt.UpdatePartials(ctx, partial)
})
}

return group.Wait()
}
Loading
Loading