As the title states it, I want to access the bitbucket API from a native Java Desktop Application. Bitbucket requires Applications to use OAuth2, and for that I found that Oltu should do the job.
However, my knowledge of OAuth is very limited and so I am stuck at a very early point. Here is what I did so far:
Step 1: I registered an OAuth Consumer with my Bitbucket Account with the following details:
Name: jerseytestapp
Description:
CallbackURL: http://localhost:8080/
URL:
Question 1: Could I automate this step?
Step 2: I ran the following Java code:
package jerseytest;
import org.apache.oltu.oauth2.client.request.OAuthClientRequest;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
public class BitbucketJersey {
public static void main(String[] args) {
OAuthClientRequest request;
try {
request = OAuthClientRequest
.authorizationLocation("https://bitbucket.org/site/oauth2/authorize")
.setClientId("jerseytestapp")
.setRedirectURI("http://localhost:8080")
.buildQueryMessage();
System.out.println(request.getLocationUri());
} catch (OAuthSystemException e) {
e.printStackTrace();
}
}
}
Step 3: I received the following locationURI and opened in Firefox
https://bitbucket.org/site/oauth2/authorize?redirect_uri=http%3A%2F%2Flocalhost%3A8080&client_id=jerseytestapp
Question 2: Do I need to use the browser or can I do this from the java application?
I receive the following answer message in Firefox:
Invalid client_id
This integration is misconfigured. Contact the vendor for assistance.
Question 3: What would be the correct next steps, and what is wrong with my approach?
Answer 1: You can automate the creation of OAuth Consumers, but you probably don’t want to.
Bitbucket provides documentation on how to create a consumer through their APIs, although the documentation is lacking many pertinent fields. Even so, you could still craft an HTTP request programmatically which mimics whatever Bitbucket's web interface is doing to create consumers. So yes, it could be automated.
Here's why you probably don't want to. In your case, you have three things that need to work together: your application, the end user, and Bitbucket. (Or in terms of OAuth jargon for this flow, those would be the client, resource owner, and authorization server, respectively.) The normal way of doing things is that your application is uniquely identified by the OAuth Consumer that you’ve created in your account, and all usages of Bitbucket by your application will use that single OAuth Consumer to identify your application. So unless you’re doing something like developing a Bitbucket application that generates other Bitbucket applications, you have no need to automate the creation of other OAuth Consumers.
Answer 2: You can authorize directly from your Java application.
Bitbucket states that it supports all four grant flows/types defined in RFC-6749. Your code is currently trying to use the Authorization Code Grant type. Using this grant type WILL force you to use a browser. But that’s not the only problem with this grant type for a desktop application. Without a public webserver to point at, you will have to use localhost in your callback URL, as you are already doing. That is a big security hole because malicious software could intercept traffic to your callback URL to gain access to tokens that the end user is granting to your application only. (See the comments on this stackoverflow question for more discussion on that topic.) Instead, you should be using the Resource Owner Password Credentials Grant type which will allow you to authenticate a Bitbucket’s username and password directly in your application, without the need of an external browser or a callback URL. Bitbucket provides a sample curl command on how to use that grant type here.
Answer 3: The correct next steps would be to model your code after the following sample. What is wrong with your approach is that you are trying to use a grant type that is ill-suited to your needs, and you are attempting to use your OAuth Consumer's name to identify your application instead of your Consumer's key and secret.
The following code sample successfully retrieved an access token with my own username/password/key/secret combination, whose values have been substituted out. Code was tested using JDK 1.8.0_45 and org.apache.oltu.oauth2:org.apache.oltu.oauth2.client:1.0.0.
OAuthClientRequest request = OAuthClientRequest
.tokenLocation("https://bitbucket.org/site/oauth2/access_token")
.setGrantType(GrantType.PASSWORD)
.setUsername("someUsernameEnteredByEndUser")
.setPassword("somePasswordEnteredByEndUser")
.buildBodyMessage();
String key = "yourConsumerKey";
String secret = "yourConsumerSecret";
byte[] unencodedConsumerAuth = (key + ":" + secret).getBytes(StandardCharsets.UTF_8);
byte[] encodedConsumerAuth = Base64.getEncoder().encode(unencodedConsumerAuth);
request.setHeader("Authorization", "Basic " + new String(encodedConsumerAuth, StandardCharsets.UTF_8));
OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient());
OAuthResourceResponse response = oAuthClient.resource(request, OAuth.HttpMethod.POST, OAuthResourceResponse.class);
System.out.println("response body: " + response.getBody());