JGit Authentication Explained
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.
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.
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.
- Extras for Eclipse: Neon Update - 6. July 2016
- What’s the Difference? Creating Diffs with JGit - 16. June 2016
- Terminate and Relaunch in Eclipse - 19. April 2016
SshTransport sshTransport = ( SshTransport )transport;
how does this downcast work? Technically i should get a ClassCastException.
And confirmed I do get one at runtime.
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 ofSshTransport
being passed.A more general
TransportConfigCallback
implementation would of course have to check the transport argument if it matches the expected type.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.
Connecting through SSH with only a password should be possible. However, your server might be configured to disallow certain authentication methods.
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.
The explanation on how to use an OAuth token with jGit was very useful, thanks for explaning!
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
Dear
Could you explain me, how to input detph parameters in CloneCommand?
JGit’s
CloneCommand
does not yet support creating shallow clones. Here is the corresponding enhancement request: https://bugs.eclipse.org/bugs/show_bug.cgi?id=475615Dear 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!
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.
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?
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.
How to disable sslVerify? when clone
As of now, JGit’s CloneCommand does not support the
http.sslVerify
setting. See here for workarounds: http://stackoverflow.com/questions/33998477/set-ssl-verification-off-for-jgit-clone-commandIs there any chance to encrypt the password, so not to set it in plaintext in the code?
Don’t try to store password in source code. This is inherently insecure. See here for more bacground: https://security.stackexchange.com/questions/52693/how-can-one-secure-a-password-key-in-source-code
If you provide more context, someone might be abl to suggest alternatives. However, this kind of general questions are best asked on forums like SO.
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.
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.
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.
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
, andid_dsa
from the current user’s.ssh
directory. UseJSsch::addIdentity
to add private keys from other locations. Note, that you need to provide a passphrase in case the key files are password protected.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 )
If you are referring to Personal Access Tokens, please read the section titled Authentication @ GitLab. Note that the access token must be encoded into the URL and given as a password in the
CredentialsProvider
. Otherwise, this Stackoverflow post may help: https://stackoverflow.com/questions/40117738/push-to-gitlab-with-jgit-authorization-errorJust wanted to say thank you for this. Really great summary, saved me I don’t know how many hours of code reading.
[…] / 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 […]
[…] 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 […]
Thanks a lot,fix my problem
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
I am using OAuth Token generated from developer settings of GITHUB Enterprise site.
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.${token} can be OAuth App or Github App or Personal Access Token? Which one is recommended?
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.
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.
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.
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,
Hello,
I wonder how to authenticate with a ssh/http repo without actually cloning the repo; just like testing a connection.
Regards,
Hello,
I’ve uploaded a Gist (https://gist.github.com/rherrmann/89ee1d5a7966f923e5e7be5da9a893a7) that shows how we use
Transport::openFetch
to test if we can connect to a remote repository without actually fetching objects.The code references a utility class
Files
used to manage temp directories that should be easy to substitute.HTH,
Rüdiger
Thank you very much Mr. Herrmann.
This is a great help.
Regards,
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 ~?
Well, perhaps the private key is invalid? Without you providing further information, we certainly won’t be able to tell.
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();
Like the exception message says, the value of
strIdRSA
that is used as an argument toaddIdentity
is invalid.Replace by other‘s id_rsa , it works ,thanks~
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);
}
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
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
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
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
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
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