Skip to content

password_expiry_utc is discarded from credential helper #1998

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

Open
Smaug123 opened this issue May 7, 2025 · 1 comment · May be fixed by #1999
Open

password_expiry_utc is discarded from credential helper #1998

Smaug123 opened this issue May 7, 2025 · 1 comment · May be fixed by #1999

Comments

@Smaug123
Copy link

Smaug123 commented May 7, 2025

Current behavior 😯

When invoking a credential helper, gix correctly calls get, but it then discards at least any password_expiry_utc before passing the result to the subsequent store invocation it correctly attempts in response to the successful use of the credential.

Expected behavior 🤔

gix should pass all details from the get stage of credential helper invocation into the store stage.

Git behavior

https://github.com/git/git/blob/6f84262c44a89851c3ae5a6e4c1a9d06b2068d75/credential.h#L187 is where Git expresses its ability to do this; it's documented at https://git-scm.com/docs/git-credential/2.41.0#Documentation/git-credential.txt-codepasswordexpiryutccode . See the "Steps to reproduce" section to see empirically what Git does.

Steps to reproduce 🕹

Preamble

Create the following script, as cred-helper.sh:

#!/bin/sh

set -e

log_request() {
    printf "Command: %s\n" "$*" >&2
    printf "Request details:\n" >&2
    while IFS= read -r line; do
        printf "%s\n" "$line" >&2
    done
}

case "$1" in
    "get")
        log_request "$@"
        printf "\nReading credentials... " >&2
        cred=$(cat ./creds.txt)
        printf "%s\n" "$cred"
        now=$(date +%s)
        printf 'password_expiry_utc=%s\n' "$((now + 10))"
        printf "quit=true\n"
        ;;
    "store")
        log_request "$@"
        ;;
    *)
        log_request "$@"
        exit 1
        ;;
esac

Make it your cred helper in some repository:

chmod u+x cred-helper.sh
git config --local credential.helper ""
git config --local credential.helper "$(pwd)/cred-helper.sh"
git config --local http.proactiveAuth basic
git remote add smaug123 https://github.com/Smaug123/test-repo.git

Generate a PAT in GitHub, and put it in creds.txt:

username=Smaug123
password=github_pat_REDACTED

Observe that git fetch smaug123 passes through the password expiry to the store command:

git fetch
Command: get
Request details:
capability[]=authtype
capability[]=state
protocol=https
host=github.com
wwwauth[]=Basic

Reading credentials... Command: store
Request details:
protocol=https
host=github.com
username=Smaug123
password=github_pat_REDACTED
password_expiry_utc=1746654451

Observe that gix does not

This is a bit harder: gix doesn't appear to respect http.proactiveAuth (though my testing wasn't super rigorous, so I might be wrong about that), so you'll have to be trying to access a private repo in here so that gix tries to use a cred helper at all (so, if you're using GitHub as the server, you'll have to make sure the GitHub PAT you generated has permission to read that repo's contents). Nor does gix seem to read the local .git/config as git does, so this has to go into your ~/.gitconfig instead:

[credential]
helper =
helper = "/path/to/cred-helper.sh"

Then Cargo.toml:

[package]
name = "git-clone"
version = "0.1.0"
edition = "2024"

[dependencies]
gix = { version = "0.72.1", features = ["worktree-mutation", "blocking-network-client", "blocking-http-transport-reqwest-rust-tls"] }

src/main.rs:

use std::path::Path;

use gix::bstr::BString;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let local_path = Path::new("/tmp/gix-smaug123");

    let url = gix::Url::from_parts(
        gix::url::Scheme::Https,
        None,
        None,
        Some("github.com".to_string()),
        None,
        BString::from("/MyOrg/MyPrivateRepo/"),
        false,
    )?;

    let mut prepared_clone = gix::prepare_clone(url, local_path)?;

    let (mut prepare_checkout, _outcome) = prepared_clone
        .fetch_then_checkout(gix::progress::Discard, &gix::interrupt::IS_INTERRUPTED)?;

    let (_, _) = prepare_checkout.main_worktree(gix::progress::Discard, &gix::interrupt::IS_INTERRUPTED)?;

    Ok(())
}

And cargo run, observing the output:

Command: get
Request details:
protocol=https
host=github.com

Reading credentials... Command: store
Request details:
url=https://github.com/REDACTED
protocol=https
host=github.com
username=Smaug123
password=REDACTED

Observe that gix has not passed through password_expiry_utc as git did.

Byron added a commit that referenced this issue May 8, 2025
@Byron Byron linked a pull request May 8, 2025 that will close this issue
4 tasks
@Byron
Copy link
Member

Byron commented May 8, 2025

Thanks a lot for reporting!

While working on it I also noticed that a couple of newer fields (about a year old) also aren't supported yet. However, I took measure to make future upgrades easier to assure it's clearer where to make modifications.

Byron added a commit that referenced this issue May 8, 2025
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.

2 participants