Skip to content

Set up git authentication when logging in to gh#2449

Merged
mislav merged 10 commits intotrunkfrom
git-credentials
Dec 15, 2020
Merged

Set up git authentication when logging in to gh#2449
mislav merged 10 commits intotrunkfrom
git-credentials

Conversation

@mislav
Copy link
Contributor

@mislav mislav commented Nov 20, 2020

This sets up git HTTPS credentials after successfully logging in to GitHub CLI. As a proof of concept, observe the last step in the flow:

$ gh auth login
? What account do you want to log into? GitHub.com
- Logging into github.com
? You're already logged into github.com as mislav. Do you want to re-authenticate? Yes
? How would you like to authenticate? Login with a web browser

! First copy your one-time code: XXXX-XXXX
- Press Enter to open github.com in your browser...
✓ Authentication complete. Press Enter to continue...

? Choose default git protocol HTTPS
- gh config set -h github.com git_protocol https
✓ Configured git protocol
? Authenticate Git with your GitHub credentials? Yes
✓ Logged in as mislav

On a fresh system, the user is now able to clone/push/pull from GitHub.com with no extra intervention, provided that they have a git credential helper defined (likely already the case on macOS and Windows).

Fixes #1434

TODO:

  • If the user selected SSH protocol, offer to upload their public keys - followup
  • Decide whether to override existing cached HTTPS credentials
  • Handle the case when a git credential helper isn't configured
  • Enable non-interactive credential priming via flag - decided against
  • Bring auth refresh up to parity with credential handling in auth login
  • Include checking git credential status in auth status - cannot safely do. Since we do not know which credential helper the user has configured, using git credential approve to read their stored credential might cause their credential helper to interactively prompt the user for credentials, and that's something we cannot allow during auth status.
  • tests

@ampinsk
Copy link

ampinsk commented Nov 23, 2020

This looks great so far! Just flagging this for later: Set up git for passwordless push/pull operations? is a bit confusing, happy to collab on the copy 👍

@mislav
Copy link
Contributor Author

mislav commented Nov 23, 2020

I've updated this branch to solve what to me was a pretty hard problem: what do we do if the user selects "HTTPS" authentication, but does not have any credential.helper configured? (I expect that, commonly, this would be Linux users on a fresh machine.)

The credential helper is a program that stores usernames & passwords for git operations over the HTTPS protocol. Git allows this to be configurable because people have different preferences for how they want to securely store authentication info. The default credential helper on macOS is osxkeychain that stores credentials in the user's login Keychain, and on Windows it's Git-Credential-Manager-Core that ships with Git for Windows. Sometimes, though, there will be no default, and we need to decide what to do.

We cannot really configure or recommend any single 3rd-party program as a credential helper because we do not know what program would be available and reliable for their platform. Git itself ships with two built-in credential helpers: cache and store, but neither are a good default in my opinion:

However, what if gh itself was a credential helper? The git credential protocol is not complex, and gh already has access to an authentication token that can be used for cloning.

In the absence of a credential helper, gh now configures this in the user's ~/.gitconfig:

[credential "https://github.com"]
        helper = !gh auth git-credential

The new (hidden) auth git-credential command now answers credential queries from git at authentication time and ensures that git clones/pulls/pushes to GitHub.com (or to a GHES instance) are authenticated with the set of credentials stored by GitHub CLI. 👌


@ampinsk Yes, let's think about alternatives! We do have to ask something, because this operation should never initiate unprompted. The gist of what we need to ask is, even though most gh operations are by now successfully authenticated, we still need to configure your git so that clone/push/pull works without a hitch.

Copy link
Contributor

@vilmibm vilmibm left a comment

Choose a reason for hiding this comment

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

Thrilled with this! I am a fan of the credential store hack 👍

if !isOurCredentialHelper(helper) {
var primeCredentials bool
err = prompt.SurveyAskOne(&survey.Confirm{
Message: "Set up git for passwordless push/pull operations?",
Copy link
Contributor

Choose a reason for hiding this comment

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

Was thinking something similar to @ampinsk - curious how something like this feels.

Suggested change
Message: "Set up git for passwordless push/pull operations?",
Message: "Also set up Git authentication for GitHub CLI operations that leverage Git?",

Copy link
Contributor Author

@mislav mislav Nov 24, 2020

Choose a reason for hiding this comment

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

@billygriffin The idea is that more git operations than just ones initiated by gh are affected. For example, after doing this step, you can safely git clone <url> (bypassing GitHub CLI) and having the credentials that you've established during gh auth login be automatically used without your intervention.

Alternatively, we could ensure that only gh-initiated git operations (e.g. git clone within gh repo clone, git push within gh pr create) are properly authenticated, but that regular git operations that you initiate outside of gh are left intact (and possibly un-authenticated). This approach would be simpler to achieve, but I think it would be worse for the user experience, because GitHub CLI often relies on regular git operations to be combined with it during everyday workflows.

Something that I've noticed in both hub and GitHub CLI: after users log into GitHub from the command line, they expect to be logged into GitHub for all their GitHub-related operations. To meet their expectations, I think that in this case it's worth leaving the boundaries of our own tool and making sure things work for them even when they were not explicitly using our tool.

Copy link

Choose a reason for hiding this comment

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

How about Authenticate Git with your GitHub credentials?

@mislav mislav marked this pull request as ready for review December 7, 2020 19:07
@mislav mislav requested review from samcoe and vilmibm December 7, 2020 19:08
Since GitHub CLI now offers to authenticate your Git as well, the token
we request here will be used for git pushes. Since we do anticipate our
users making edits to their GitHub Actions workflow files, we want them
to be able to push their changes, and this scope allows that.
Copy link
Contributor

@samcoe samcoe left a comment

Choose a reason for hiding this comment

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

This looks great to me! I left a couple comments/questions, but nothing blocking.

cmd := &cobra.Command{
Use: "git-credential",
Args: cobra.ExactArgs(1),
Short: "Implements git credential helper protocol",
Copy link
Contributor

Choose a reason for hiding this comment

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

Are we worried at all about this protocol updating and us having to maintain/upkeep this implementation?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The oldest reference to this specific format of the git credential protocol that I could find is from 2012, and it hasn't changed since. It's also very unlikely that git will ever change it—at least not in a backwards-incompatible way—because it's in git's strong interest to avoid breaking current programs that have implemented the protocol.

func helperRun(opts *CredentialOptions) error {
if opts.Operation == "store" {
// We pretend to implement the "store" operation, but do nothing since we already have a cached token.
return cmdutil.SilentError
Copy link
Contributor

Choose a reason for hiding this comment

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

Rather than a silent error here is it possible to send back a response saying that the user should use gh auth login/refresh to do this operation? This seems like it would only be called if a user is explicitly trying set credentials from the git command, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For a user, that would make sense! But the user doesn't ever interact with this helper directly. Instead, git invokes it internally when git credential is interacted with, and the user typically doesn't even interact with git credential themselves. So I don't think a more helpful error would be useful here.

Furthermore, when multiple credential helpers are configured, git currently uses them in a sort of a chained sequence, taking credentials from one and storing them into all others. This is why I felt it was important to implement store, but also to discard any attempt to actually store credentials, since those unrelated to GitHub we don't want to store, and those for GitHub we already have.

Copy link

@neilalb16 neilalb16 left a comment

Choose a reason for hiding this comment

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

restore

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.

Prime user's git credentials after authenticating with gh

6 participants