Git2-rs username does not match previous request

Hello

I'm trying to use git2 without success.

I'm trying to clone a repository

#[cfg(test)]
mod test {

    use super::*;

    #[test]
    fn test_git_connection() {
        let mut callbacks = git2::RemoteCallbacks::new();
        callbacks.credentials(move |_url, _username_from_url, _allowed| {
            println!("Authenticating for URL: {}", _url);
            println!("Username from URL: {:?}", _username_from_url);
            println!("Allowed types: {:?}", _allowed);

            Ok(Cred::ssh_key_from_agent("git@github.com")
                .expect("Could not get ssh key from ssh agent"))
        });
        let mut builder = git2::build::RepoBuilder::new();
        builder.bare(true);
        let mut fetch_opts = git2::FetchOptions::new();
        fetch_opts.remote_callbacks(callbacks);
        builder.fetch_options(fetch_opts);

        let url = "git@github.com:Its-Just-Nans/termpic.git";
        println!("Cloning {}", url);
        let _repo = match builder.clone(url, &PathBuf::from("termpic")) {
            Ok(repo) => repo,
            Err(e) => {
                eprintln!("Error: {:?}", e);
                return;
            }
        };
    }
}

Output

Cloning git@github.com:Its-Just-Nans/termpic.git
Authenticating for URL: git@github.com:Its-Just-Nans/termpic.git
Username from URL: Some("git")
Allowed types: CredentialType(SSH_KEY | SSH_MEMORY | SSH_CUSTOM)
Error: Error { code: -1, klass: 23, message: "username does not match previous request" }

Do you guys have any ideas ?

Thanks

Just a guess - what does this change result in?

-        callbacks.credentials(move |_url, _username_from_url, _allowed| {
+        callbacks.credentials(move |_url, username_from_url, _allowed| {
             println!("Authenticating for URL: {}", _url);
-            println!("Username from URL: {:?}", _username_from_url);
+            println!("Username from URL: {:?}", username_from_url);
             println!("Allowed types: {:?}", _allowed);

-            Ok(Cred::ssh_key_from_agent("git@github.com")
+            let username = username_from_url.unwrap_or("git");
+            Ok(Cred::ssh_key_from_agent(username)
                .expect("Could not get ssh key from ssh agent"))
        });

(Or just use "git" instead of "git@github.com".)

1 Like

Thanks for that!

But how does the Cred::ssh_key_from_agent know which key to use for which git provider ?

short answer is, the agent doesn't need to know the "username", it's the server who checks it, and a key id is picked by the server if multiple keys are available.

here's an (over-simplified) description of the ssh authentication process using public keys:

first, the ssh client send the login user name to the server, and the server should send back allowed authentication methods, e.g. password, and/or certain public key algorithms such as RSA2048.

then, the client send a "list identities" request to ssh-agent, which will provide all the public keys that are loaded into the agent, and this list is then sent to the server.

then, the ssh server will check the provided public keys, and if one of the key is recognized (e.g. listed in user's ~/.ssh/authorized_keys), it will send some challenge data to the client, along with the picked public key.

then, the client request the agent to sign the challenge using the corresponding private key, and send the signed data back to the server.

finally, the server checks the signature and establish the requested communication tunnels, which are used as redirected pipes of the remote program to be executed (e.g. user's shell or git)

1 Like

Clear as glass

Thanks for explanation

So I guess in the end I misunderstood Cred::ssh_key_from_agent(username) as it's more like a theorical

Cred::default().with_username("git").ssh_key_from_agent()

NOTE this doen't exists !!

^^

1 Like