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

Auth in extraheader makes it difficult to override #162

Closed
peter-evans opened this issue Feb 24, 2020 · 5 comments
Closed

Auth in extraheader makes it difficult to override #162

peter-evans opened this issue Feb 24, 2020 · 5 comments

Comments

@peter-evans
Copy link
Contributor

Description

Since v2 auth is being persisted in git config, which is great, and makes it much easier to perform git commands. However, persisting the auth in a git extraheader config option makes it difficult to override. My particular use case is an action I maintain that creates pull requests for changes made during the workflow. The relevant part of which is that the action accepts a token passed to it and uses it to push to a branch. That token may, or may not, be the same token that was used to checkout the repository. Due to this, I must override whatever auth has been set by actions/checkout and push using the token provided.

Initially the action was setting the token in the URL like this:

git push https://x-access-token:<token>@github.com/owner/my-repo.git

However, the Authorization header set by extraheader take precedence.

Next, I decided to try overriding the extraheader in local git config like this:

git -c "http.https://github.com/.extraheader=AUTHORIZATION: basic <basic_credential>" push

Initially, this seemed to work, but then I discovered that what was actually happening was instead of overriding the extraheader git adds two AUTHORIZATION headers. This actually makes sense and I was mistaken for thinking it would override the existing extraheader config option. What is interesting about this is that the main Git service accepts these requests, but git-lfs does not. You can read the discussion I had about this at the git-lfs repository here: git-lfs/git-lfs#4031

So the only option I was left with to override the auth was to unset the extraheader when the action starts, and restore the extraheader in local config when the action completes. It feels kind of "hacky" and I don't like that I need to mess with config set by actions/checkout.

Possible solution

During the discussion at the git-lfs repository, @bk2204 suggested that this could be solved with a credential helper.

So I tested, assuming that actions/checkout set auth in a custom credential helper instead of extraheader.

git config credential.https://github.com/.helper "! f() { echo username=x-access-token; echo password=$GITHUB_TOKEN; };f"

This then allows subsequent git calls to override the auth either by setting the token in the URL, or setting an extraheader with the git -c option. Both of these methods take precedence and it only falls back to the credential helper when auth hasn't been explicitly set.

I'm sure changing the auth mechanism is the last thing you want to do, but I wondered what are your thoughts about this and whether you think it's worth considering switching to a credential helper for the next major version bump.

@ericsciple
Copy link
Contributor

ericsciple commented Feb 24, 2020

i believe you can override the extra header with a more specific url. Either http.https://github.com/owner/my-repo.git/.extraheader or http.https://github.com/owner/my-repo/.extraheader (match your clone url, but keep the trailing slash before .extraheader)

@peter-evans
Copy link
Contributor Author

@ericsciple Thanks for your reply. I didn't think of trying a more specific URL so I did some further testing. What I've discovered is that git and git-lfs clients have different ways of handling multiple extraheader config options.

First I tried setting a more specific URL as follows.

GIT_TRACE=1 GIT_TRANSFER_TRACE=1 GIT_CURL_VERBOSE=1 git \
  -c "http.https://github.com/owner/my-repo/.extraheader=AUTHORIZATION: basic <basic_credential>" \
  push https://github.com/owner/my-repo

The git client still adds two Authorization headers, but when the test includes Git LFS tracked files the git-lfs client allows this more specific URL to override the local config and sets one Authorization header.

I then read the git documentation a bit closer and it suggests you can reset the list of extraheader options by setting an empty value.

http.extraHeader

Pass an additional HTTP header when communicating with a server. If more than one such entry exists, all of them are added as extra headers. To allow overriding the settings inherited from the system config, an empty value will reset the extra headers to the empty list.

https://git-scm.com/docs/git-config#Documentation/git-config.txt-httpextraHeader

So I tried to reset the extraheader list as follows.

GIT_TRACE=1 GIT_TRANSFER_TRACE=1 GIT_CURL_VERBOSE=1 git \
  -c "http.https://github.com/.extraheader=" \
  -c "http.https://github.com/.extraheader=AUTHORIZATION: basic <basic_credential>" \
  push https://github.com/owner/my-repo

As the documentation suggested, the extraheader list is cleared for the git client and it only sends one Authorization header. However, the git-lfs client still picks up both extraheader options and sends two Authorization headers. So it doesn't seem to have the same behaviour and won't clear the list of extraheader with an empty value.

In summary:

  • git: The client will not override http.<url>.extraheader with a more specific URL and will add all extraheaders that match remote.origin.url (or a remote URL supplied directly in the command). Setting an empty value will clear the extraheader list.
  • git-lfs: The client will override http.<url>.extraheader if a more specific URL is given. Setting an empty value will not clear the extraheader list.

So in order to satisfy the way that both clients work, it seems I need to clear the list with an empty value for git, and then pass an extraheader with a more specific URL than the one in local config for git-lfs.

This works as a method to override the local config for both git and git-lfs clients. Only one Authorization header is sent by both clients.

GIT_TRACE=1 GIT_TRANSFER_TRACE=1 GIT_CURL_VERBOSE=1 git \
  -c "http.https://github.com/.extraheader=" \
  -c "http.https://github.com/owner/my-repo/.extraheader=AUTHORIZATION: basic <basic_credential>" \
  push https://github.com/owner/my-repo

Although it feels a bit awkward, I think this is a better solution than unsetting and restoring the local git config.

cc: @bk2204

@bk2204
Copy link

bk2204 commented Feb 26, 2020

I think a custom credential helper is the right tool here. The extraheader code is designed to deal with things like cookies and Azure DevOps's insistence on using Bearer instead of Basic authentication for OAuth tokens. It's not designed to supplant Git's native authentication handling.

Using a custom credential helper also makes it really easy to override: with the example I proposed, you simply change the value of GITHUB_TOKEN for one command and things work natively. And we know that both Git and Git LFS handle the credential helpers in the same way.

I plan to patch Git to support Bearer tokens in the future, at which point there really won't be any reason to use extraheader for authentication in the future.

@ericsciple
Copy link
Contributor

Intentionally not using cred helper due to issues dealt with in the past, apple git caching and not calling the cred helper every time. The built-in token is generated per job. For self hosted runner, every job should use the new token sent down with that job.

@piotr-yuxuan
Copy link

Hello, would you consider using GIT_ASKPASS, or the config equivalent?

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 a pull request may close this issue.

4 participants