Skip to content
Open
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
32 changes: 31 additions & 1 deletion cmd/account/link_key/link_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,23 @@ func (h *handler) Execute(ctx context.Context, in Inputs) error {
)
return fmt.Errorf("missing required flags for --non-interactive mode")
}
label, err := ui.Input("Provide a label for your owner address")
defaultLabel := h.defaultOwnerLabel()

title := "Provide a label for your owner address"
if defaultLabel != "" {
title = fmt.Sprintf("%s [%s]", title, defaultLabel)
}

label, err := ui.Input(
title,
ui.WithDefaultValue(defaultLabel),
ui.WithValidate(func(s string) error {
if strings.TrimSpace(s) == "" {
return fmt.Errorf("a label is required")
}
return nil
}),
)
if err != nil {
return err
}
Expand Down Expand Up @@ -408,6 +424,20 @@ func (h *handler) checkIfAlreadyLinked() (bool, error) {
return false, nil
}

// defaultOwnerLabel derives a suggested label from the authenticated user's
// email address (the portion before the "@"). Returns "" if no email is available.
func (h *handler) defaultOwnerLabel() string {
email, err := h.credentials.GetEmail()
if err != nil || email == "" {
return ""
}

if idx := strings.Index(email, "@"); idx > 0 {
return email[:idx]
}
return email
}

func (h *handler) displayDetails() {
ui.Line()
ui.Title("Linking web3 key to your CRE organization")
Expand Down
25 changes: 25 additions & 0 deletions internal/credentials/credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,31 @@ func (c *Credentials) GetOrgID() (string, error) {
return orgID, nil
}

// GetEmail returns the authenticated user's email address from the access token.
// It is not available for API key authentication.
func (c *Credentials) GetEmail() (string, error) {
if c.AuthType == AuthTypeApiKey {
return "", fmt.Errorf("email is not available for API key authentication")
}

claims, err := c.decodeJWTClaims()
if err != nil {
return "", err
}

// The email claim is namespaced (e.g. "https://api.cre.chain.link/email"),
// so find it by looking for any key ending with "email".
for key, value := range claims {
if strings.HasSuffix(key, "email") {
if email, ok := value.(string); ok && email != "" {
return email, nil
}
}
}

return "", fmt.Errorf("email claim not found in access token")
}

// GetDeploymentAccessStatus returns the deployment access status for the organization.
// This can be used to check and display whether the user has deployment access.
func (c *Credentials) GetDeploymentAccessStatus() (*DeploymentAccess, error) {
Expand Down
27 changes: 24 additions & 3 deletions internal/ui/prompts.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,10 @@ func Confirm(title string, opts ...ConfirmOption) (bool, error) {
type InputOption func(*inputConfig)

type inputConfig struct {
description string
placeholder string
description string
placeholder string
defaultValue string
validate func(string) error
}

// WithInputDescription sets the description for an Input prompt.
Expand All @@ -98,14 +100,30 @@ func WithPlaceholder(placeholder string) InputOption {
}
}

// WithDefaultValue pre-fills the Input prompt with the given value, so pressing
// Enter without editing submits the default.
func WithDefaultValue(value string) InputOption {
return func(c *inputConfig) {
c.defaultValue = value
}
}

// WithValidate sets a validation function for an Input prompt. The form
// will not submit until the function returns nil.
func WithValidate(validate func(string) error) InputOption {
return func(c *inputConfig) {
c.validate = validate
}
}

// Input displays a single text input prompt and returns the entered value.
func Input(title string, opts ...InputOption) (string, error) {
cfg := inputConfig{}
for _, o := range opts {
o(&cfg)
}

var result string
result := cfg.defaultValue
input := huh.NewInput().
Title(title).
Value(&result)
Expand All @@ -116,6 +134,9 @@ func Input(title string, opts ...InputOption) (string, error) {
if cfg.placeholder != "" {
input = input.Placeholder(cfg.placeholder)
}
if cfg.validate != nil {
input = input.Validate(cfg.validate)
}

form := huh.NewForm(
huh.NewGroup(input),
Expand Down
Loading