Skip to content

gh-scoped-creds can't coexist with credential helpers that use useHttpPath = true #76

@ryanlovett

Description

@ryanlovett

Bug description

gh-scoped-creds cannot coexist with other Git credential helpers that require useHttpPath = true, such as git-credential-github-app. When both are configured in the same environment, gh-scoped-creds fails to provide credentials, and Git prompts the user for username/password.

This prevents JupyterHub deployments from offering both:

  • Pull access from organization private repos (via git-credential-github-app or similar)
  • Push access to user repos (via gh-scoped-creds)

How to reproduce

  1. Configure a system-level credential helper that requires useHttpPath = true in /srv/conda/etc/gitconfig:

    [credential "https://github.com"]
      helper = !git-credential-github-app --app-key-file /path/to/key.pem --app-id 12345
      useHttpPath = true
  2. Install gh-scoped-creds and run the authentication flow:

    import gh_scoped_creds
    %ghscopedcreds --client-id <your-client-id>
  3. Attempt to clone a private repository that the user has access to:

    git clone https://github.com/username/private-repo

Expected behaviour

Git should use the gh-scoped-creds OAuth token to authenticate and successfully clone the repository without prompting for credentials.

Actual behaviour

Git prompts for username and password:

Username for 'https://github.com/username/private-repo':

The gh-scoped-creds token is never used.

Root Cause

When gh-scoped-creds configures Git, it uses Git's built-in credential-store helper:

subprocess.check_call([
    "git", "config", "--global",
    "credential.https://github.com.helper",
    f"store --file={temp_file}"
])

This stores credentials as:

https://x-access-token:[email protected]

The problem occurs because:

  1. The system-level useHttpPath = true setting applies globally to all credential helpers
  2. When Git queries for credentials with useHttpPath = true, it includes the repository path:
    protocol=https
    host=github.com
    path=username/private-repo
    
  3. Git's credential-store helper performs exact matching and requires the path to match
  4. Since gh-scoped-creds stores credentials without a path (because one OAuth token works for all repos), there's no match
  5. The credential helper returns nothing, and Git falls back to prompting the user

Why Workarounds Don't Work

  1. Can't disable useHttpPath - Other credential helpers like git-credential-github-app require it and crash without the path field
  2. Can't store credentials with a path - gh-scoped-creds creates a single OAuth token for all repos, not per-repo credentials
  3. Can't modify git credential-store - It's part of Git itself and we can't change its matching behavior

Your personal set up

Container image at https://github.com/berkeley-dsep-infra/biology-user-image.

  • OS: Ubuntu 22.04
  • Version(s):
    • gh-scoped-creds: 4.1
    • JupyterHub: 5.3.0
    • git: 2.38.1
Full environment
gh-scoped-creds==4.1
jupyterhub==5.3.0
Configuration

See https://github.com/berkeley-dsep-infra/datahub/blob/staging/deployments/biology/config/common.yaml#L132.

System gitconfig (/srv/conda/etc/gitconfig):

[credential "https://github.com"]
  helper = !git-credential-github-app --app-key-file /srv/conda/etc/github/github-app-private-key.pem --app-id 229235
  useHttpPath = true

User gitconfig after running %ghscopedcreds (~/.gitconfig):

[credential "https://github.com"]
  helper = store --file=/tmp/tmpXXXXXXXX

Effective configuration:

$ git config --list --show-origin | grep credential
file:/srv/conda/etc/gitconfig   credential.https://github.com.helper=!git-credential-github-app ...
file:/srv/conda/etc/gitconfig   credential.https://github.com.usehttppath=true
file:/home/jovyan/.gitconfig    credential.https://github.com.helper=store --file=/tmp/tmpXXXXXXXX

Both helpers are present, but the useHttpPath = true setting prevents the store helper from matching.

Logs

Testing credential retrieval directly:

$ echo -e "protocol=https\nhost=github.com\npath=username/private-repo" | git credential fill
Username for 'https://github.com/username/private-repo':

The credential helpers return nothing, so Git prompts for credentials.

Testing the store helper directly:

$ cat /tmp/tmpXXXXXXXX
https://x-access-token:[email protected]

$ echo -e "protocol=https\nhost=github.com\npath=username/private-repo" | git credential-store --file=/tmp/tmpXXXXXXXX get
# No output - no match found because path doesn't match

Proposed Solution

Replace the use of Git's built-in credential-store with a custom credential helper that:

  1. Reads the OAuth token from the temporary file (maintaining current security model)
  2. Ignores the path parameter when queried by Git
  3. Returns the token for any GitHub repository

This would allow gh-scoped-creds to work alongside other credential helpers that require useHttpPath = true.

I can submit a PR with the implementation details if you think this is the desired approach.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions