github.com/abdfnx/gh-api@v0.0.0-20210414084727-f5432eec23b8/pkg/cmd/auth/refresh/refresh.go (about) 1 package refresh 2 3 import ( 4 "errors" 5 "fmt" 6 7 "github.com/AlecAivazis/survey/v2" 8 "github.com/MakeNowJust/heredoc" 9 "github.com/abdfnx/gh-api/internal/authflow" 10 "github.com/abdfnx/gh-api/internal/config" 11 "github.com/abdfnx/gh-api/pkg/cmd/auth/shared" 12 "github.com/abdfnx/gh-api/pkg/cmdutil" 13 "github.com/abdfnx/gh-api/pkg/iostreams" 14 "github.com/abdfnx/gh-api/pkg/prompt" 15 "github.com/spf13/cobra" 16 ) 17 18 type RefreshOptions struct { 19 IO *iostreams.IOStreams 20 Config func() (config.Config, error) 21 22 MainExecutable string 23 24 Hostname string 25 Scopes []string 26 AuthFlow func(config.Config, *iostreams.IOStreams, string, []string) error 27 28 Interactive bool 29 } 30 31 func NewCmdRefresh(f *cmdutil.Factory, runF func(*RefreshOptions) error) *cobra.Command { 32 opts := &RefreshOptions{ 33 IO: f.IOStreams, 34 Config: f.Config, 35 AuthFlow: func(cfg config.Config, io *iostreams.IOStreams, hostname string, scopes []string) error { 36 _, err := authflow.AuthFlowWithConfig(cfg, io, hostname, "", scopes) 37 return err 38 }, 39 MainExecutable: f.Executable, 40 } 41 42 cmd := &cobra.Command{ 43 Use: "refresh", 44 Args: cobra.ExactArgs(0), 45 Short: "Refresh stored authentication credentials", 46 Long: heredoc.Doc(`Expand or fix the permission scopes for stored credentials 47 48 The --scopes flag accepts a comma separated list of scopes you want your gh credentials to have. If 49 absent, this command ensures that gh has access to a minimum set of scopes. 50 `), 51 Example: heredoc.Doc(` 52 $ gh auth refresh --scopes write:org,read:public_key 53 # => open a browser to add write:org and read:public_key scopes for use with gh api 54 55 $ gh auth refresh 56 # => open a browser to ensure your authentication credentials have the correct minimum scopes 57 `), 58 RunE: func(cmd *cobra.Command, args []string) error { 59 opts.Interactive = opts.IO.CanPrompt() 60 61 if !opts.Interactive && opts.Hostname == "" { 62 return &cmdutil.FlagError{Err: errors.New("--hostname required when not running interactively")} 63 } 64 65 if runF != nil { 66 return runF(opts) 67 } 68 return refreshRun(opts) 69 }, 70 } 71 72 cmd.Flags().StringVarP(&opts.Hostname, "hostname", "h", "", "The GitHub host to use for authentication") 73 cmd.Flags().StringSliceVarP(&opts.Scopes, "scopes", "s", nil, "Additional authentication scopes for gh to have") 74 75 return cmd 76 } 77 78 func refreshRun(opts *RefreshOptions) error { 79 cfg, err := opts.Config() 80 if err != nil { 81 return err 82 } 83 84 candidates, err := cfg.Hosts() 85 if err != nil { 86 return fmt.Errorf("not logged in to any hosts. Use 'gh auth login' to authenticate with a host") 87 } 88 89 hostname := opts.Hostname 90 if hostname == "" { 91 if len(candidates) == 1 { 92 hostname = candidates[0] 93 } else { 94 err := prompt.SurveyAskOne(&survey.Select{ 95 Message: "What account do you want to refresh auth for?", 96 Options: candidates, 97 }, &hostname) 98 99 if err != nil { 100 return fmt.Errorf("could not prompt: %w", err) 101 } 102 } 103 } else { 104 var found bool 105 for _, c := range candidates { 106 if c == hostname { 107 found = true 108 break 109 } 110 } 111 112 if !found { 113 return fmt.Errorf("not logged in to %s. use 'gh auth login' to authenticate with this host", hostname) 114 } 115 } 116 117 if err := cfg.CheckWriteable(hostname, "oauth_token"); err != nil { 118 var roErr *config.ReadOnlyEnvError 119 if errors.As(err, &roErr) { 120 fmt.Fprintf(opts.IO.ErrOut, "The value of the %s environment variable is being used for authentication.\n", roErr.Variable) 121 fmt.Fprint(opts.IO.ErrOut, "To refresh credentials stored in GitHub CLI, first clear the value from the environment.\n") 122 return cmdutil.SilentError 123 } 124 return err 125 } 126 127 var additionalScopes []string 128 129 credentialFlow := &shared.GitCredentialFlow{} 130 gitProtocol, _ := cfg.Get(hostname, "git_protocol") 131 if opts.Interactive && gitProtocol == "https" { 132 if err := credentialFlow.Prompt(hostname); err != nil { 133 return err 134 } 135 additionalScopes = append(additionalScopes, credentialFlow.Scopes()...) 136 } 137 138 if err := opts.AuthFlow(cfg, opts.IO, hostname, append(opts.Scopes, additionalScopes...)); err != nil { 139 return err 140 } 141 142 if credentialFlow.ShouldSetup() { 143 username, _ := cfg.Get(hostname, "user") 144 password, _ := cfg.Get(hostname, "oauth_token") 145 if err := credentialFlow.Setup(hostname, username, password); err != nil { 146 return err 147 } 148 } 149 150 return nil 151 }