Igor Kromin |   Consultant. Coder. Blogger. Tinkerer. Gamer.

I've been working on the next version of Extrudifier that will have integration to Thingiverse for direct uploads and Thing creations right from the Extrudifier app. This needed a bit of work to get the OAuth2 workflow set up, this is what I describe in this post. I chose to go with Google App Engine and Servlets because this was the easy and free and could be up and running quite quickly. All that's needed to implement this integration is a bit of JavaScript, a Java Servlet and some App Engine magic.

These are some pages for reference: Thingiverse Developers page and Googel App Engine Java Documentation. I will not be explaining things like what are Servlets or how to write the whole HTML page that's being used to initiate the authentication, this is assumed knowledge for this write up.

The first thing to do is register the app on the Thingiverse site. This will give us two things:
  • Client ID
  • Secret Key

Both of these are required on the Servlet side to complete the OAuth2 workflow. The Secret Key should never be used on the client side, hence the name: secret, only the server should know about it.



Note: it is very important to set the callback URL in the application because this is where Thingiverse will be sending back a code parameter that is required to complete the workflow. This should be the URL of the Servlet.

So now that we have the Client ID and the Secret Key, we can make a button that will initiate the workflow. This button will use a bit of JavaScript to do the actual work.
<button onclick="thingiPublish();" type="button">


When the button is clicked, the thingiPublish() function is called. Here's what it looks like:
function thingiPublish() {
var data = 'param1=somevalue&param2=othervalue';
var url = 'https://www.thingiverse.com/login/oauth/authorize?client_id=XXX&' + data;
window.open(url);
}


The code is very simple, all it does is create a URL to the Thingiverse server and opens it up in a new window to initiate the OAuth2 workflow. This URL encodes some parameters that we wish to pass to our Servlet, notably the param1 and param2 URL parameters; of course these do not need to be specified if your Servlet does not require them.

Note: The client_id parameters must be set to your application's Client ID instead of XXX as is in the example code above.

At this point we have enough functionality to get initiate communication with the Thingiverse server, which will generate a code for us to use, which it will send to our Servlet. So now we have to create this Servlet to process the code and talk to the Thingiverse server again to retrieve an Access Token, this will complete the OAuth2 workflow.

The Servlet will need some constants defined:
private static final String THINGI_AUTH_URL = "https://www.thingiverse.com/login/oauth/access_token";
private static final String THINGI_SECRET = "XXX";
private static final String THINGI_CLIENTID = "XXX";


The Secret and Client ID must be set to your application's respective values.

The Servlet will implement the doGet() method.

Note: The code is using the URLConnection classes instead of the Apache HTTP Client because Google App Engine will not allow the Apache code to run since it requires sockets connections, whereas the standard JDK classes work.
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
String param1 = req.getParameter("param1");
String param2 = req.getParameter("param2");
String code = req.getParameter("code");
URL url = new URL(THINGI_AUTH_URL);
HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
DataOutputStream cgiInput;
urlConn.setDoInput(true);
urlConn.setDoOutput(true);
urlConn.setUseCaches(false);
urlConn.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
// Send request
cgiInput = new DataOutputStream(urlConn.getOutputStream());
String content = "client_id=" + THINGI_CLIENTID
+ "&client_secret=" + THINGI_SECRET
+ "&code=" + code;
cgiInput.writeBytes(content);
cgiInput.flush();
cgiInput.close();
// Read response
BufferedReader cgiOutput =
new BufferedReader(new InputStreamReader(urlConn.getInputStream()));
StringBuilder sb = new StringBuilder();
String line = null;
while (null != (line = cgiOutput.readLine())){
sb.append(line);
}
cgiOutput.close();
urlConn.disconnect();
// check output for the access_token and redirect back
String output = sb.toString();
if (output.indexOf("access_token=") != -1) {
resp.sendRedirect("callback.html?" + output + "&" +
"param1=" + param1 + "&" +
"param2=" + param2);
}
else {
log.severe("OAUTH2 Failed: " + output);
resp.sendRedirect("error.html");
}
}


So that's the Servlet code, but what does it do exactly? First of all, the code gets the URL parameters that have been passed into the Servlet, specifically it looks for the param1, param2 and code parameters. The code parameter has been generated by the Thingiverse server, the others are the parameters that we originally passed to Thingiverse.

Then the code sets up a HttpURLConnection to the Thingiverse access token URL and sends some data to it as a HTML form. The data is sent using the GET method and we pass in the Client ID, the Secret and the Code.

Once the request is sent, the code reads the response and closes the connection. The response is checked to make sure it contains the access_token parameter. Since Thingiverse sends the response in a URL encoded format, there is no need to process it.

If the response is ok, the code builds another URL that will receive the access token and the other parameters we passed in originally, in this case it's the callback.html page. The Servlet simply redirects to this page.

If there was an error, the Servlet redirects to the error.html page instead.

That completes the OAuth2 workflow. The callback.html page will have the access_token now which can be used to access the Thingiverse API. It's quite simple to retrieve this using a bit of JavaScript, this write up doesn't go into that detail however.

-i

A quick disclaimer...

Although I put in a great effort into researching all the topics I cover, mistakes can happen. Use of any information from my blog posts should be at own risk and I do not hold any liability towards any information misuse or damages caused by following any of my posts.

All content and opinions expressed on this Blog are my own and do not represent the opinions of my employer (Oracle). Use of any information contained in this blog post/article is subject to this disclaimer.
Hi! You can search my blog here ⤵
NOTE: (2022) This Blog is no longer maintained and I will not be answering any emails or comments.

I am now focusing on Atari Gamer.