JGit Authentication Explained

Home  >>  Eclipse  >>  JGit Authentication Explained

JGit Authentication Explained

On December 9, 2014, Posted by , In Eclipse, By ,,, , With 50 Comments

Authentication in JGit is mostly on par with native Git. Commonly used protocols like SSH and HTTP(S) and their authentication methods are supported. This article summarizes how to use the JGit authentication API to access remote Git repositories securely.

Though the examples in this article use the CloneCommand, the described techniques can be applied to all classes that connect to remote repositories like FetchCommand, PushCommand, LsRemoteCommand, etc. All of these commands have a common base class – TransportCommand – which offers the methods discussed here.

HTTP(S) – https://example.com/repo.git

Authenticating via HTTP and HTTPS is straightforward. An implementation of CredentialsProvider is used to return the authentication credentials when the command requests them. The CredentialsProvider to be used for a certain command is specified through setCredentialsProvider().

For example, the code below clones a repository over HTTPS and authenticates with username and password.

CloneCommand cloneCommand = Git.cloneRepository();
cloneCommand.setURI( "https://example.com/repo.git" );
cloneCommand.setCredentialsProvider( new UsernamePasswordCredentialsProvider( "user", "password" ) );

The UsernamePasswordCredentialsProvider is an implementation of CredentialsProvider that comes with JGit and uses the given username and password to authenticate.

Alternatively, JGit (version 3.5 and later) can also read credentials from the user’s .netrc file. The NetRCCredentialsProvider uses the first machine entry from the file for authentication.

Though it is not recommendable to send credentials over unsecured connections, the described approach also works for plain HTTP connections like http://example.com/repo.git.


As of now, the CloneCommand does not consider the http.sslVerify configuration setting when connecting via https. SSL certificates and host names will always be verified.

This prevents JGit from being able to clone from servers that use self-signed certificates out of the box. See this SO post for how to work around this limitation.

SSH with Public Key – ssh://user@example.com/repo.git

JGit delegates creating and destroying SSH connections to the abstract SshSessionFactory. To use public key authentication for an SSH connection, such a session factory has to be specified for the executed command.

With setTransportConfigCallback(), a TransportConfigCallback interface can be specified to intercept the connection process. Its sole method – named configure() – is called just before a connection is established. It is passed a parameter of type Transport which will be used to copy objects between the local and the remote repository. For each protocol, there is a distinct subclass of Transport that handles the respective details of that protocol.

Like shown below the callback can be used to configure the Transport instance right before it is put into use:

SshSessionFactory sshSessionFactory = new JschConfigSessionFactory() {
  @Override
  protected void configure( Host host, Session session ) {
    // do nothing
  }
};
CloneCommand cloneCommand = Git.cloneRepository();
cloneCommand.setURI( "ssh://user@example.com/repo.git" );
cloneCommand.setTransportConfigCallback( new TransportConfigCallback() {
  @Override
  public void configure( Transport transport ) {
    SshTransport sshTransport = ( SshTransport )transport;
    sshTransport.setSshSessionFactory( sshSessionFactory );
  }
} );

JGit provides an abstract JSchConfigSessionFactory that uses JSch to establish SSH connections and requires its configure() to be overridden. Because in the simplest case there isn’t anything to be configured, the example above just overrides the method to let the code compile.

See also  More Units with MoreUnit

JSchConfigSessionFactory is mostly compatible with OpenSSH, the SSH implementation used by native Git. It loads the known hosts and private keys from their default locations (identity, id_rsa and id_dsa) in the user’s .ssh directory.

If your private key file is named differently or located elsewhere, I recommend to override createDefaultJSch(). After calling the base method, custom private keys can be added like so:

@Override
protected JSch createDefaultJSch( FS fs ) throws JSchException {
  JSch defaultJSch = super.createDefaultJSch( fs );
  defaultJSch.addIdentity( "/path/to/private_key" );
  return defaultJSch;
}

In this example a private key from a custom file location is added. If you look into the JSch JavaDoc you will find further overloaded addIdentity() methods.

In particular, if the private key is password-protected, a passphrase can be specified with addIdentity( "/path/to/private_key", "passphrase" ).

For the sake of completeness, I should mention that there is also a global session factory. It can be obtained and changed through SshSessionFactory.get/setInstance() and is used as a default if no specific shSessionFactory was configured for a command. However, I recommend refraining from using it. Apart from making it harder to write isolated tests, there might be code outside of your control that changes the global session factory.

SSH with Password – ssh://user@example.com/repo.git

As with using SSH with public keys, an SshSessionFactory must be specified to use password-secured SSH connections. But this time, the session factory’s configure() method has a purpose.

SshSessionFactory sshSessionFactory = new JschConfigSessionFactory() {
  @Override
  protected void configure( Host host, Session session ) {
    session.setPassword( "password" );
  }
} );

CloneCommand cloneCommand = Git.cloneRepository();
cloneCommand.setURI( "ssh://user@example.com/repo.git" );
cloneCommand.setTransportConfigCallback( new TransportConfigCallback() {
  @Override
  public void configure( Transport transport ) {
    SshTransport sshTransport = ( SshTransport )transport;
    sshTransport.setSshSessionFactory( sshSessionFactory );
  }
} );

A JSch session represents a connection to an SSH server and in line 4, the password for the current session is set. The rest of the code is the same that was used to connect via SSH with public key authentication.

Which Authentication Method to Use?

Some authentication methods discussed here can also be combined. For example, setting a credentials provider while attempting to connect to a remote repository via SSH with public-key won’t harm. However, you usually want to know what Transport will be used for a given repository-URL beforehand.

To determine that, use the TransportProtocol’s canHandle() method. It returns true if the protocol can handle the given URL and false otherwise. A list of all registered TransportProtocols can be obtained from Transport.getTransportProtocols(). And once the protocol is known, the appropriate authentication method can be chosen.

Authentication @ GitHub

GitHub supports a variety of protocols and authentications methods, but certainly not all possible combinations. A common mistake, for example, is to try to use SSH with password authentication. But this combination is not supported – only SSH with public keys is.

This comparison of protocols offered by GitHub lists what is supported and what not. Summarized, there is:

  • Plain Git (e.g. git://github.com/user/repo.git): The transfer is unencrypted and the server is not verified.
  • HTTPS (e.g. https://github.com/user/repo.git): Works practically everywhere. Uses password authentication for pushing but allows anonymous fetch and clone.
  • SSH (e.g. ssh://git@github.com:user/repo.git): Uses public key authentication, also for fetch and clone.
See also  Code Centric Trainings with the Samples Manager

Please note one thing if you are accessing a GitHub repository through HTTPS with an OAuth access token. The token does not need to be specified in the URL but only given as a user name. The example below illustrates this:

command.setURI( "https://github.com/user/repo.git" );
command.setCredentialsProvider( new UsernamePasswordCredentialsProvider( "token", "" ) );

Authentication @ GitLab

GitLab offers slightly different authentication methods.

Of course, SSH is also among them (e.g. git@gitlab.com:user/repo.git). It uses public key authentication and, like at GitHub, it can be used to fetch and push.

HTTPS with username and password (e.g. https://gitlab.com/user/repo.git) is also supported. It uses password authentication for pushing but allows anonymous fetch and clone. The username and password need to be passed through a UsernamePasswordCredentialsProvider like this:

command.setCredentialsProvider( new UsernamePasswordCredentialsProvider( "username", "password" ) );

In addition, the GitLab Profile Settings allow to create Personal Access Tokens. These can also be used to authenticate HTTPS connections. Access tokens can have an expiration date or can be revoked manually at any time.

In order to use an access token, it must be encoded into the URL and given as a password as shown below:

command.setURI( "https://gitlab-ci-token:a1b2..x7z@gitlab.com/user/repo.git" );
command.setCredentialsProvider( new UsernamePasswordCredentialsProvider( "username", "a1b2..x7z" ) );

Concluding JGit Authentication

While I find the authentication facilities are a bit widely scattered over the JGit API, they get the task done. The recipes given here hopefully provide you with the necessary basics to authenticate connections in JGit and to hide the complexities of the API could be seen as an exercise to practice clean code ;)

In order to help with setting up the development environment, you may want to also read An Introduction to the JGit Sources. If you have difficulties or further questions, please leave a comment or ask the friendly and helpful JGit community for assistance.

Rüdiger Herrmann
Follow me
Latest posts by Rüdiger Herrmann (see all)

50 Comments so far:

  1. Saurav Tapader says:

    SshTransport sshTransport = ( SshTransport )transport;

    how does this downcast work? Technically i should get a ClassCastException.

    And confirmed I do get one at runtime.

    • Rüdiger Herrmann says:

      You probably didn’t specify an ssh URL. The actual Transport subclass that is passed to TransportConfigCallback#configure() depends on the URL. In the example given in the post, the cast succeeds because an ssh URL (ssh://user@example.com/repo.git) was specified that results in an instance of SshTransport being passed.

      A more general TransportConfigCallback implementation would of course have to check the transport argument if it matches the expected type.

      • Vikrant Sonoone says:

        Hey .Thanks for this Post, a quick question – if I use “SSH with Password – ssh://user@example.com/repo.git” then also do I need to have public key and private key? I want to just use password without keys. Is it possible?

        I am getting the following exception:

        com.jcraft.jsch.JSchException: SSH_MSG_DISCONNECT: 2 Session has timed out waiting for authentication after 120000 ms.

        • Rüdiger Herrmann says:

          Connecting through SSH with only a password should be possible. However, your server might be configured to disallow certain authentication methods.

  2. Saurav Tapader says:

    Indeed, my remote url was https. And thus it was generating an instance of TransportHttp resulting in the ClassCastException.

    Overriding the remote to ssh solved the prob.

  3. Martijn says:

    The explanation on how to use an OAuth token with jGit was very useful, thanks for explaning!

  4. Houssem says:

    thanks for your tutorial, but i have always an Auth fail ..
    i posted a question on stackoverflow :
    http://stackoverflow.com/questions/33410092/using-ssh-authentication-with-jgit-to-access-a-git-repository-securely-auth-fa

    can u take a look please ? thanks

  5. Dapper says:

    Dear

    Could you explain me, how to input detph parameters in CloneCommand?

  6. Vijay says:

    Dear All,

    I want to clone/pull/checkout remote project to my local in java.

    And I want to checkout a particular tag at my local machine.

    Below are the codes I am using

    try {
    SshSessionFactory sshSessionFactory = new JschConfigSessionFactory() {
    @Override
    protected void configure(Host arg0, Session arg1) {
    // TODO Auto-generated method stub
    }
    @Override
    protected JSch createDefaultJSch(FS fs) throws JSchException {
    // TODO Auto-generated method stub
    return super.createDefaultJSch(fs);
    }
    };

    CloneCommand cloneCommand = Git.cloneRepository();
    cloneCommand.setURI( “ssh://XXX.git” );
    cloneCommand.setTransportConfigCallback( new TransportConfigCallback() {
    @Override
    public void configure( Transport transport ) {
    SshTransport sshTransport = ( SshTransport )transport;
    sshTransport.setSshSessionFactory( sshSessionFactory );
    }

    } );

    cloneCommand.setDirectory(new File(“C:/XXX/TempProject”));
    cloneCommand.call();

    } catch (GitAPIException e) {
    e.printStackTrace();
    }
    Please help!

    • Rüdiger Herrmann says:

      Please note that we aren’t able to maintain a please-solve-my-problem form here. You may want to post a question on StackOverflow that includes what the expected outcome is, what you’ve tried so far, and what the actual outcome is.

      • Vijay says:

        Thanks for your reply Rudiger.

        I am using URI : ssh://XXX.git

        Caused by: com.jcraft.jsch.JSchException: Auth fail
        at com.jcraft.jsch.Session.connect(Session.java:512)
        at org.eclipse.jgit.transport.JschConfigSessionFactory.getSession(JschConfigSessionFactory.java:117)
        … 10 more

        Could you please tell me how to fix it?

        • Rüdiger Herrmann says:

          I am afraid, no. With the few information, that you gave I am unable to tell what is going on.

          And again, this is no Q&A forum. Use StackOverflow for such questions.

  7. velly says:

    How to disable sslVerify? when clone

  8. thama says:

    Is there any chance to encrypt the password, so not to set it in plaintext in the code?

  9. Rehman says:

    Hello Rüdiger Herrmann,

    Thank you for your quick response earlier.
    I am newbie to eclipse as well as GitLab.
    I need to push some files to gitlab using jgit. I have initiated git. Now I need to push file using SSH. Unfortunately, I can not understand very well from the material on the internet.
    I need to make a simple program that pushes the local repository to Remote.
    I somehow can not relate the remote URI , key and Push() method.

    I would really appreciate if you could send me a simple sample code that explains adding key, adding remote URI and pushing.

    Looking forward !

    Best Regards.

    • Rüdiger Herrmann says:

      I am afraid, from the few information that you give, I cannot guess where exactly your problem is. Please provide more context, i.e. what are you trying to achive, what have you tried, what is the actuall outcome, what is the expected outcome.

      Please also note, that this is not a Q&A forum. You might be better off asking such questions on SO or similar forums. Chances are, that more people are reading your post.

  10. Oscar Wu says:

    The ssh key implementation doesn’t work for me, My senario is we have intenal stash server, I use “ssh-add” add my private key to agent and use you sample code to clone repo from stash server , I tried “ssh://git@company.com:user_name/group/repo” or “ssh://user_name@company.com:user_name/group/repo”, However They both didn’t work.

    • Rüdiger Herrmann says:

      JGit uses JSch to connect through SSH. Please read the JSch documentation for details about SSH agents. By default JGit reads private keys from identity, id_rsa, and id_dsa from the current user’s .ssh directory. Use JSsch::addIdentity to add private keys from other locations. Note, that you need to provide a passphrase in case the key files are password protected.

  11. Liz says:

    How can I authenticate using private-token in jgit for gitlab?I am able to authenticate using username and password but I need to use private -token. Below didn’t work as it uses personal access token

    CredentialsProvider credentialsProvider
    = new UsernamePasswordCredentialsProvider( “myname”, “” );
    command.setURI( “https://gitlab-ci-token:@gitlab.com/myname/repo.git” )
    command.setCredentialsProvider( credentialsProvider )

  12. Michael Toy says:

    Just wanted to say thank you for this. Really great summary, saved me I don’t know how many hours of code reading.

  13. Utilisation de l'authentification SSH avec JGit pour accéder à un référentiel Git en toute security / échec d'authentification FR Git says:

    […] / config ( Ma machine => Machine Amazon AWS => Serveur Stach Git ), Donc le code ce tutoriel: http://www.codeaffine.com/2014/12/09/jgit-authentication/ mot bien, merci […]

  14. Using SSH authentication with JGit to Access a Git Repository Securely / Auth fail Git Baby says:

    […] file ( My machine => Amazon AWS Machine => Stach Git Server ) , So the code in this tutorial : http://www.codeaffine.com/2014/12/09/jgit-authentication/ word fine, thanks […]

  15. songxin says:

    Thanks a lot,fix my problem

  16. Nipun says:

    I am trying to authenticate github enterprise as follow:
    Git git = Git.cloneRepository().setURI(JGitConstants.repositoryURI)
    .setCredentialsProvider(new UsernamePasswordCredentialsProvider(“${token}”,””))
    .setDirectory(JGitConstants.localRepositoryDir).call();
    git.close();
    and it’s giving me org.eclipse.jgit.api.errors.TransportException: : not authorized

    • Nipun says:

      I am using OAuth Token generated from developer settings of GITHUB Enterprise site.

    • Rüdiger Herrmann says:

      For GitHub Enterprise I can’t say, but at least for a hosted GitHub repository, the actual token value must be given as the user name. In your snippet, replace ${token} with the actual token value.

      • Nipun says:

        ${token} can be OAuth App or Github App or Personal Access Token? Which one is recommended?

        • Rüdiger Herrmann says:

          To connect to a hosted GitHub repository, it would be the OAuth access token (see the Authentication @ GitHub section in this article). However, I don’t know how or if authentication with GitHub Enterprise differs from hosted GitHub. You would have to try out or read the documentation.

  17. Dave Musicant says:

    Now that Apache MINA sshd is the path going forward for JGit, instead of JSch, do you have any plans to update this blog entry (or post a new one) showing how to similarly authenticate with Apache MINA sshd? I’ve been looking at org.eclipse.egit.core.internal.EgitSshdSessionFactory.java as an example, but would nonetheless love to find something more expository.

    • Rüdiger Herrmann says:

      While I think it would be interesting write a follow up post, I can’t foresee if/when I will find the time for it.
      However, I you would consider writing a guest post about authenticating with JGit and Apache MINA, feel free to contact me through private mail.

  18. Raafat says:

    Hi, thanks for your article. It did explain a lot but I still have a question: Why does git from the command line use cached credentials, but JGit could not.
    Could you please check my SO question: https://stackoverflow.com/questions/56374564/jgit-requires-authentication-but-git-on-the-commandline-doesnt

    Thanks,

  19. Abdur says:

    Hello,

    I wonder how to authenticate with a ssh/http repo without actually cloning the repo; just like testing a connection.

    Regards,

  20. yjxj1ban says:

    com.jcraft.jsch.JSchException: invalid privatekey: [B@38ddd429
    at com.jcraft.jsch.KeyPair.load(KeyPair.java:664) ~[jsch-0.1.53.jar:na]
    at com.jcraft.jsch.KeyPair.load(KeyPair.java:561) ~[jsch-0.1.53.jar:na]
    at com.jcraft.jsch.IdentityFile.newInstance(IdentityFile.java:40) ~[jsch-0.1.53.jar:na]
    at com.jcraft.jsch.JSch.addIdentity(JSch.java:407) ~[jsch-0.1.53.jar:na]
    at com.jcraft.jsch.JSch.addIdentity(JSch.java:367) ~[jsch-0.1.53.jar:na]
    at qa.git.util.GitClone$1$1.createDefaultJSch(GitClone.java:61) ~[classes/:na]

    ===> what’s wrong ~?

    • Rüdiger Herrmann says:

      Well, perhaps the private key is invalid? Without you providing further information, we certainly won’t be able to tell.

      • yjxj1ban says:

        JGit not compatibility with new version of SSH~?
        https://stackoverflow.com/questions/53134212/invalid-privatekey-when-using-jsch

        the core code here:

        CloneCommand cloneCmd = Git.cloneRepository().setURI(remoteUrl);
        ……
        cloneCmd.setTransportConfigCallback(new TransportConfigCallback() {
        @Override
        public void configure(Transport transport) {
        SshTransport sshTransport = (SshTransport) transport;

        sshTransport.setSshSessionFactory(new JschConfigSessionFactory() {
        @Override
        protected void configure(OpenSshConfig.Host host, Session session) {
        session.setConfig(“StrictHostKeyChecking”, “no”);
        }
        @Override
        protected JSch createDefaultJSch( FS fs) throws JSchException {
        JSch jsch = super.createDefaultJSch( fs );
        jsch.removeAllIdentity();
        jsch.addIdentity(strIdRSA);
        return jsch;
        }
        });

        }
        });
        cloneCmd.call();

  21. Rüdiger Herrmann says:

    Like the exception message says, the value of strIdRSA that is used as an argument to addIdentity is invalid.

  22. yjxj1ban says:

    Replace by other‘s id_rsa , it works ,thanks~

  23. Sathish says:

    HI
    I used Personal Access Tokens to do authentication for Git. And I generated token from https://github.pie.apple.com/settings/tokens and got a key. Using that I performed the authentication. But I ran into the below exception though I followed the same steps as mentioned above.

    org.eclipse.jgit.api.errors.TransportException: git@github.pie.apple.com:aos-met/modules.git: invalid privatekey: [B@4d81a136

    Did I miss something in the below code ?

    // Returns a command object to execute a clone command with URI to clone from
    CloneCommand clone = Git.cloneRepository().setURI(remote).setDirectory(new File(local));
    // Creating Git object with associated repository
    git = clone.setBranch(branchName).setCredentialsProvider(getCredentialProvider(gitUserName,gitToken)).call();
    boolean success = isLocalRepoWithGitExists(git.getRepository().getDirectory().getPath());
    if (success) {
    logger.info(“Cloned repo:{} into: localPath {}”, remote, local);
    }

  24. Chethan says:

    Hi Rudriger,

    I am trying to connect internal https repo using gitlab private token
    CredentialsProvider credentialsProvider = new
    UsernamePasswordCredentialsProvider( “PRIVATE-TOKEN”, );
    git.push()
    .setCredentialsProvider( credentialsProvider )
    .call();

    CloneCommand cloneCommand = Git.cloneRepository();
    cloneCommand.setURI( “https://example.com/repo.git” );
    cloneCommand.setCredentialsProvider(credentialsProvider );

    Note: followed below steps
    https://stackoverflow.com/questions/40117738/push-to-gitlab-with-jgit-authorization-error
    Stack trace:
    TransportException : connection to https://example.com/repo.git could not be established because of ssl problems
    final caused by sun.security.provider.certpath.SunCertPathBuilderException : unable to find valid certification path to request target

  25. Rüdiger Herrmann says:

    If your internal GitLab service uses self-signed certs, perhaps these answer could help: https://stackoverflow.com/questions/9210514/unable-to-find-valid-certification-path-to-requested-target-error-even-after-c

  26. Abdur Billah says:

    Hi Rüdiger,

    You have been very helpful in resolving my issues around JGit. My current issue is not related to gitlab, But I thought I can use this thread to get some help from you on JGit merge conflicts.
    Here is the UseCase
    # Got into merge conflicts when trying to merge (Recursive) using JGit API.
    # I have the numberOfConflicts in each file available from MergeResults. (so far so good)
    # However, I did not resolve my conflicts at the moment and got logged out – as a result all merge conflict info got lost.
    # I logged back and ran the status and got hold of the conflicting files as
    Status status = mGit.status().call();
    conflictedFiles = status.getConflicting();
    # Question: Is there any way to retrieve the numberOfConclicts in each of the conflicted files above (conflictedFiles) using JGit APIs?

    Thank you very much.

    -Abdur

  27. Rüdiger Herrmann says:

    Hi Abdur,

    MergeResult::getConflicts returns a map of paths to an array of ints that indicate the number of conflicts per path. See the JavaDoc how to decode this information.

    If you have further questions I recommend to use Stackoverflow or the official JGit support channels (http://eclipse.org/jgit/support). Your question will be seen by a much larger group of people.

    Best,
    Rüdiger

  28. Abdur Billah says:

    Hi Rüdiger,

    Thank you for taking time to write a reply for this.
    Yes, MergeResult::getConflicts() returns the number of conflicts and I’m using that. at the time of conflicts.
    However, my useCase is slightly different as I explained.
    When I do not have a MergeResult object at hand (as I did not resolve the merge conflicts and came back at a later time when MergeResult object is no longer available).

    I wonder whether it is possible to create/get an instance of MergeResult from only the conflicting file as follows:
    Status status = mGit.status().call();
    conflictedFiles = status.getConflicting();

    I’ll post it at http://eclipse.org/jgit/support) as suggested.

    Best regards and than you very much.

    -abdur

  29. Chethan says:

    Hi Rüdiger, this is with regards to earlier post cloning the private git repo using jgit api you had asked me to refer one of the urls
    ” If your internal GitLab service uses self-signed certs, perhaps these answer could help: https://stackoverflow.com/questions/9210514/unable-to-find-valid-certification-path-to-requested-target-error-even-after-c
    by looking at that we had create sslcontext and trying to create a scoket and start before calling close in between we do following things
    CloneCommand cloneCommand = Git.cloneRepository();
    cloneCommand.setURI( “https://example.com/repo.git” );
    cloneCommand.setCredentialsProvider( new UsernamePasswordCredentialsProvider( “user”, “password” ) );
    even then we are getting “Unable to find valid certification path to requested target” we can dont konw how to load these certificate at runtime so that during cloning it will refer can you suggest me some steps or links