Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@vilmibm
Copy link
Contributor

@vilmibm vilmibm commented Jul 28, 2020

This PR adds gh auth login.

interactive mode - paste token

Peek 2020-07-31 17-25

interactive mode - browser flow

Peek 2020-07-31 17-24

non-interactive mode

authloginsstdin

Part of #1413

Open questions:

  • I'm not sure what support for the device auth flow looks like and was hoping follow up with that later; is that reasonable?
  • We're not set up to test the browser auth flow. Should I hack in support for that or continue to rely on manual testing?
  • For the survey-based tests, I didn't test the printing of informational messages to STDERR. I figured just ensuring that the correct config was written given a set of prompt stubs was good enough. Is it?

@mislav mislav mentioned this pull request Jul 28, 2020
4 tasks
@vilmibm vilmibm force-pushed the auth-cmd branch 2 times, most recently from cd8edcf to 9cbcab2 Compare July 29, 2020 21:41
@vilmibm vilmibm marked this pull request as ready for review July 29, 2020 21:53
@vilmibm vilmibm requested review from ampinsk and mislav July 29, 2020 21:53
Copy link
Contributor

@mislav mislav left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's great that we're finally going to have this command 🎉

{
name: "hostname set",
opts: &LoginOptions{
Hostname: "rebecca.chambers",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

☣️

Comment on lines +199 to +223
fmt.Fprintln(opts.IO.ErrOut)
fmt.Fprintln(opts.IO.ErrOut, heredoc.Doc(`
Tip: you can generate a Personal Access Token here https://github.com/settings/tokens
The minimum required scopes are 'repo' and 'read:org'.`))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit, but I'd be fine with printing information like this to stdout since it's part of the essential output of the login command. I do not expect that people will be redirecting stdout of login to a script or anyting

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm torn; on the one hand, printing to STDOUT is absolutely fine here (and survey is using STDOUT). On the other, we print this kind of stuff to STDERR in other commands and I'd like to be consistent.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the other, we print this kind of stuff to STDERR in other commands and I'd like to be consistent.

True. But the way I see it, the only reason we separate out "special" notices from regular output is because we expect that the user might be piping regular output to a file or a script. If the user is not doing that (for this command, they are not likely to), there is no incentive for us to print notices on a separate stream.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some software writes to the tty in raw mode for these types of informational notices, so the text is neither in stdout nor stderr.

@mislav
Copy link
Contributor

mislav commented Jul 30, 2020

  • I'm not sure what support for the device auth flow looks like and was hoping follow up with that later; is that reasonable?

Yes, I think device flow should absolutely be a follow-up. The device flow can be feature-detected and fits under the "Login with a web browser” flow as far as the user is concerned, so its implementation will not require almost any UI tweaks.

  • We're not set up to test the browser auth flow. Should I hack in support for that or continue to rely on manual testing?

Uh, I declared bankruptcy on testing the browser auth flow since I did not have any ideas how to stub it back then. If you do, that would be awesome! Especially because after this, I would really like us to opensource the auth package so hub can use it as well.

  • For the survey-based tests, I didn't test the printing of informational messages to STDERR. I figured just ensuring that the correct config was written given a set of prompt stubs was good enough. Is it?

I think that's good enough, especially since we might be tweaking the flow still. Credentials being correctly saved is most of what matters

@vilmibm
Copy link
Contributor Author

vilmibm commented Jul 30, 2020

note to self: do some bolding / green check / formatting on browser flow prompts

@vilmibm vilmibm changed the title WIP gh auth login gh auth login Jul 31, 2020
@vilmibm vilmibm changed the base branch from trunk to ghe-api July 31, 2020 22:12
@vilmibm vilmibm force-pushed the auth-cmd branch 2 times, most recently from f33828f to d1d310d Compare July 31, 2020 22:15
@vilmibm vilmibm requested a review from mislav July 31, 2020 22:17
@vilmibm
Copy link
Contributor Author

vilmibm commented Jul 31, 2020

@mislav

I've...

  • rebased on ghe-api branch
  • properly validate scopes now (both in terms of handling admin:read and using the correct host)
  • made the login command create its own http client from an in-memory config
  • streamlined config interactions (only one write per command invocation, now)
  • added check for already having a valid authentication for the given host

Base automatically changed from ghe-api to trunk August 3, 2020 15:19
Copy link
Contributor

@mislav mislav left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything looks great; thank you!

The only potential blocker from my standpoint is GITHUB_TOKEN handling. As it stands, this branch only respects configuration from ~/.config/gh/hosts.yml file, but if a user supplied an invalid or revoked GITHUB_TOKEN via their environment, any attempt of a gh command will fail with a HTTP 401, but gh auth login will show “You're already logged into github.com as mislav. Do you want to re-authenticate?” And even if I went through re-authentication process, a new token will be written to my config file, but the old (broken) GITHUB_TOKEN from environment still takes precedence and sabotages my gh operations.

Comment on lines +71 to +74
defer opts.IO.In.Close()
token, err := ioutil.ReadAll(opts.IO.In)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure, but I think the logic to actually read the token from stdin might belong in loginRun instead of here. Typically, we used RunE so far to just to argument/flags processing, but avoided executing any command logic. I'm on the fence about this, so I could go either way. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was very unsure. I think this falls under the category of argument parsing if you consider the value on STDIN to be an argument. I felt that the "inputs" of this command were a hostname and a token and that loginRun should only care about those two things and be less concerned with whence they came. I decided to see how uncomfortable the testing felt and that turned out to feel fine so I'm content to leave it like this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That reasoning makes sense!

Comment on lines +199 to +223
fmt.Fprintln(opts.IO.ErrOut)
fmt.Fprintln(opts.IO.ErrOut, heredoc.Doc(`
Tip: you can generate a Personal Access Token here https://github.com/settings/tokens
The minimum required scopes are 'repo' and 'read:org'.`))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the other, we print this kind of stuff to STDERR in other commands and I'd like to be consistent.

True. But the way I see it, the only reason we separate out "special" notices from regular output is because we expect that the user might be piping regular output to a file or a script. If the user is not doing that (for this command, they are not likely to), there is no incentive for us to print notices on a separate stream.

@danshearer
Copy link
Contributor

This is an excellent improvement. Thanks!

Also, the wee screencasts let me see the entire scope of what you are trying to do in seconds. Great.

@vilmibm vilmibm mentioned this pull request Aug 6, 2020
Copy link
Contributor

@mislav mislav left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a minor point about GITHUB_TOKEN handling, but would be also fine if we addressed various authentication issues and error messaging around GITHUB_TOKEN in a follow-up. 🌟

return nil
}

var clientFromCfg = func(hostname string, cfg config.Config) (*api.Client, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason why this and validateHostCfg are overridable functions (i.e. assigned to a var)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clientFromCfg is overriden in tests; validateHostCfg is not and that's just vestigial, I'll fix that.


// Returns whether or not scopes are present, appID, and error
func (c Client) HasScopes(wantedScopes ...string) (bool, string, error) {
url := "https://api.github.com/user"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's great that this no longer depends on the /user endpoint!

}

return false, appID, nil
if !search["read:org"] && !search["admin:org"] {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love the simplicity of this new implementation for checking scopes 🏅


opts.Token = strings.TrimSpace(string(token))
} else if ghToken != "" {
opts.Token = ghToken
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that making GITHUB_TOKEN here indistinguishable from the one passed via --with-token is not the behavior that I would expect. That could result in GITHUB_TOKEN being stored to hosts.yml, which I don't think should ever happen since GITHUB_TOKEN is environment-only.

It's useful to think of GITHUB_TOKEN as credentials stored in a virtual layer "above" (and entirely separate of) hosts.yml. So I think that gh auth login/status should be aware of GITHUB_TOKEN being set and help you determine whether the credentials are valid, but they can't help you interactively refresh the credentials since only the user can change an environment variable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That could result in GITHUB_TOKEN being stored to hosts.yml, which I don't think should ever happen since GITHUB_TOKEN is environment-only.

I was going quickly and didn't even think about this 🤦‍♀️ I will at least push a commit to not save the config if GITHUB_TOKEN is being validated.

return errors.New("empty hostname would leak oauth_token")
}

err := cfg.Set(opts.Hostname, "oauth_token", opts.Token)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This only stores oauth_token key for a host and not the user key, but maybe that's fine since we don't really need the user key for anything other than reassurance for people manually inspecting their config file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, that's a good point though; I might as well set that.

@vilmibm vilmibm merged commit 813ac79 into trunk Aug 6, 2020
leodica added a commit to leodica/cli that referenced this pull request Aug 6, 2020
Merge pull request cli#1445 from cli/auth-cmd
@tierninho
Copy link
Contributor

@vilmibm Curious if we need a URL validation on this as it accepts jibberish and even a blank space

? What account do you want to log into? GitHub Enterprise
? GHE hostname: ghsdfsdf
- Logging into ghsdfsdf
? How would you like to authenticate? Login with a web browser
- Press Enter to open ghsdfsdf in your browser...

The app hangs as the browser just goes to a dead page as expected.

Happy to open an issue if needed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants