Skip to content

gh as Docker credential helper #5150

Open

Description

@odsod

Describe the feature or problem you’d like to solve

The current way of creating PATs for authentication to private GHCR images is cumbersome. Especially if GHCR is adopted in an enterprise, it adds extra steps to employee onboarding.

Proposed solution

Add gh auth configure-docker (similar to how gcloud does it) and have Docker use gh as a credential helper for ghcr.io.

Additional context

See https://cloud.google.com/sdk/gcloud/reference/auth/configure-docker for inspiration of a setup that works really well.

Activity

  1. mislav commented on Feb 1, 2022

    @mislav
    Contributor

    Hi, thank you for the suggestion!

    and have Docker use gh as a credential helper for ghcr.io.

    This is a great idea. PRs welcome! Tip: you can duplicate the existing gitcredential package as a starting point for the docker credential helper implementation.

  2. added and removed on Feb 1, 2022
  3. hirasawayuki commented on Feb 3, 2022

    @hirasawayuki
    Contributor

    I'd like to attempt to this issue! Can I be assigned to this issue?😀

  4. hirasawayuki commented on Feb 7, 2022

    @hirasawayuki
    Contributor

    I have a question. Is there a way to get a token with write:packages permission?

    I tried docker login using the token I got from cfg.GetWithSource.

    token, _, err = cfg.GetWithSource(hostname, "oauth_token")

    $ dokcer login succeeds, but I can't seem to pull and push image from ghcr.io.

    After investigating the cause, I found out that write:packages permission is required.
    However, the token got from cfg.GetWithSource does not seem to have write:packages permission.

    If you have a better way, please let me know 🙇‍♂️

  5. mislav commented on Feb 8, 2022

    @mislav
    Contributor

    This is blocked on the fact that GitHub CLI's own OAuth token cannot be used to authenticate when pulling containers from ghcr.io: #5174 (comment)

    Until this is solved on GitHub, gh auth configure-docker won't be possible. In the meantime, use manually-generated PATs with docker login ghcr.io --username <user> --password-stdin as before.

  6. added
    platformProblems with the GitHub platform rather than the CLI client
    and removed on Feb 8, 2022
  7. srl295 commented on Mar 18, 2022

    @srl295

    This is blocked on the fact that GitHub CLI's own OAuth token cannot be used to authenticate when pulling containers from ghcr.io: #5174 (comment)

    Just for the record, this is no longer blocked on #5174, but that issue was folded into this one.

  8. imjasonh commented on Apr 4, 2022

    @imjasonh

    You can gh auth login --scopes=write:packages, which at least adds that scope to the token.

    After that, to make a Docker cred helper, you'll need to provide an executable named docker-credential-ghcr in $PATH, which expects to be passed the string "ghcr.io" on stdin, and produces the following JSON to stdout:

    {
      "Username": "my-username",
      "Secret": "my-gh-pat"
    }
    

    A minimal Go example of the above:

    package main
    
    import (
            "encoding/json"
            "io"
            "log"
            "os"
            "strings"
    
            "gopkg.in/yaml.v2"
    )
    
    func main() {
            // Read auth token from config file.
            b, err := os.ReadFile("/Users/jasonhall/.config/gh/hosts.yml")
            if err != nil {
                    log.Fatal(err)
            }
            var m map[string]struct {
                    User       string `yaml:"user"`
                    OAuthToken string `yaml:"oauth_token"`
            }
            if err := yaml.Unmarshal(b, &m); err != nil {
                    log.Fatal(err)
            }
            if _, ok := m["github.com"]; !ok {
                    log.Fatal("no github.com auth configured")
            }
            if got, err := io.ReadAll(os.Stdin); err != nil {
                    log.Fatal(err)
            } else if strings.TrimSpace(string(got)) != "ghcr.io" {
                    log.Fatalf("bad input; got %q", string(got))
            }
            json.NewEncoder(os.Stdout).Encode(map[string]string{
                    "Username": m["github.com"].User,
                    "Secret":   m["github.com"].OAuthToken,
            })
    }
    

    That could either be a separate executable, or just a special mode of invoking gh if it can tell it's symlinked to $PATH/docker-credential-ghcr (based on os.Args[0]).

    That symlink could be created as part of gh auth configure-docker, in addition to adding the "ghcr.io": "ghcr" line to "credHelpers" in ~/.docker/config.json, and maybe even upgrading the token to ensure it has the write:packages scope.

    However, when I set all this up myself, I get the following error trying to write images to ghcr.io:

    permission_denied: The token provided does not match expected scopes.
    

    So it looks like there's still something missing to get the gh token to include the write:packages scope.

    Is there some way to determine the scopes granted to a PAT, given only the PAT? Google OAuth supports a /tokeninfo endpoint which can confirm that the token includes the write:packages scope?

  9. srl295 commented on Apr 4, 2022

    @srl295

    @imjasonh or to whom it may concern,

    You can gh auth login --scopes=write:packages, which at least adds that scope to the token.

    If there is progress on this, please make sure that as a user I can select read:packages as an option. I might want to only use this feature to read and not to write.

    Does write:packages imply read:packages? If not, do you need to use --scopes=read:packages,write:packages or something for your use?

  10. 28 remaining items

  11. pkit commented on Jan 11, 2024

    @pkit

    I'll rephrase, what does having a credential helper provide over and above logging in as in #5150 (comment) ?

    It provides automatic credential handling/refresh across the whole docker ecosystem.
    I.e. it works in docker, compose, all related tools and APIs.
    And it also manages the lifecycle of the key through "store", "erase" protocol commands.

  12. evilhamsterman commented on Feb 20, 2024

    @evilhamsterman

    @pkit I don't think the lifecycle commands are necessary as they are managed by gh so they'd be noops like the GCR credential manager. But it would definitely be helpful to handle the secure storage and token refresh.

  13. baughmann commented on Mar 30, 2024

    @baughmann

    This worked off the bat for me:

    $ gh auth token | docker login ghcr.io --user YOURUSER --password-stdin
    $ docker pull ghcr.io/someuser/somerepo:sometag

    Very helpful! Sidenote for me, this was actually --username not --user:

    gh auth token | docker login ghcr.io --username YOURUSER --password-stdin 
  14. MattPChoy commented on Dec 7, 2024

    @MattPChoy

    The above didn't work for me:

    $ docker pull ghcr.io/username/reponame:tag
    
    What's next:
        View a summary of image vulnerabilities and recommendations → docker scout quickview ghcr.io/username/reponame:tag
    Error response from daemon: denied
  15. adrianisk commented on Jan 7, 2025

    @adrianisk

    Very helpful! Sidenote for me, this was actually --username not --user:

    gh auth token | docker login ghcr.io --username YOURUSER --password-stdin

    You can also pull the username automatically if you want to add this to a shared script

    gh auth token | docker login ghcr.io --username $(gh api user --jq '.login') --password-stdin
    
  16. MikaelElkiaer commented on Jan 22, 2025

    @MikaelElkiaer

    I just stumbled upon this: https://github.com/bradschwartz/docker-credential-ghcr-login

    Can't believe how easy it would have been to implement.
    Unfortunately, it is far more complicated for NuGet...

  17. anttiharju commented on Jan 9, 2026

    @anttiharju

    Summarising what works as of writing:

    $ gh auth login --scopes read:packages
    $ gh auth token | docker login ghcr.io --username "$(gh api user --jq '.login')" --password-stdin
    $ docker pull ghcr.io/my-org/private-image:latest
  18. babakks commented on Jan 9, 2026

    @babakks
    Member

    FWIW if you're using v2.81.0 or later, you can avoid gh api call in favour of gh auth status --json output:

    _cred="$(gh auth status --active --show-token --hostname github.com --json hosts --jq '.hosts."github.com"[0]')"
    docker login ghcr.io --username "$(jq -r .login <<< "$_cred")" --password "$(jq -r .token <<< "$_cred")"
    
    # or via stdin to avoid `ps aux` leak
    _cred="$(gh auth status --active --show-token --hostname github.com --json hosts --jq '.hosts."github.com"[0]')"
    echo "$(jq -r .token <<< "$_cred")" | docker login ghcr.io --username "$(jq -r .login <<< "$_cred")" --password-stdin

    There'd be a API call under the hood (to validate the token), but it's a safer with multi-account setups.

  19. imjasonh commented on Jan 9, 2026

    @imjasonh

    The suggestions above get the token and pass it to docker login, which stores a copy of the token in Docker's credential store.

    This will work fine, but if you gh auth logout, gh auth switch, gh auth refresh --reset-scopes or gh auth login without --scopes read:packages, the token in Docker's cred store may not be the same as the one gh has, which may cause confusion.

    The original request, to have gh auth token or similar called when Docker needs credentials to ghcr.io, would close this gap, since Docker's cred store wouldn't provide the token at all, and it would always delegate to gh.

    It sounds like, in order to have gh be used as a cred helper smoothly, we'd also need gh's default set of scopes to include read:packages. If updating the default set of scopes is too cumbersome, perhaps gh auth configure-docker (name TBD) could request re-login with those scopes as part of setting itself up as a cred helper.

  20. MikaelElkiaer commented on Jan 14, 2026

    @MikaelElkiaer

    I still think this is a solid solution: https://github.com/MikaelElkiaer/docker-credential-ghcr-login

    It is a real Docker credential helper that simply uses gh cli - it even checks whether the required scope is set.

  21. anttiharju commented on Jan 17, 2026

    @anttiharju

    I'd be wary of unofficial solutions for anything auth-related.

  22. chrheg commented on Jan 29, 2026

    @chrheg

    You can use this as well: https://gist.github.com/mislav/e154d707db230dc882d7194ec85d79f6 , just a small shell script 👍

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementa request to improve CLIgh-authrelating to the gh auth commandpitchpitched internally for prioritisation

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions