package io; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; import org.apache.commons.io.Charsets; import org.json.JSONException; import org.json.JSONObject; import support.ConsumerKeySecret; /** * An API requester used for application-only requests. Not all requests are * allowed, see https://dev.twitter.com/docs/auth/application-only-auth for * details. * * @author Peter Wu */ public class BearerRequester extends AbstractRequester { /** * A base64-encoded access token that authenticates requests. */ private final String access_token; /** * Instantiates a requester with the given access token. The caller should * test the token for validity. * * @param access_token The bearer token that authenticates a request. */ public BearerRequester(String access_token) { this.access_token = access_token; } /** * Instantiates an instance, requesting an access token from the provided * consumer key and secret. * * @param secrets The consumer secrets provided by Twitter. * @throws IOException if a failure occurred while acquiring a token. */ public BearerRequester(ConsumerKeySecret secrets) throws IOException { String postData = "grant_type=client_credentials"; URL url = buildUrl("oauth2/token"); HttpURLConnection conn = open(url); try { conn.setRequestMethod("POST"); // set request headers conn.addRequestProperty("Authorization", "Basic " + secrets.getBearerTokenBase64()); conn.addRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); conn.setFixedLengthStreamingMode(postData.length()); // connect and send request conn.setDoOutput(true); conn.getOutputStream().write(postData.getBytes(Charsets.UTF_8)); try { JSONObject resp = getResponseAsJson(conn); // TODO: parse resp.errors if (!resp.getString("token_type").equals("bearer")) { throw new IOException("Expected bearer token type"); } access_token = resp.getString("access_token"); } catch (JSONException ex) { // treat JSON errors as if an I/O error occurred throw new IOException(ex); } } finally { conn.disconnect(); } } @Override protected void preconnect(URLConnection conn) throws IOException { // requests do not have to be signed, instead rely on the Bearer token conn.addRequestProperty("Authorization", "Bearer " + access_token); } /** * @return the access token that authenticates requests. */ public String getAccessToken() { return access_token; } @Override public boolean isValid() throws IOException { // NOTE: this actually contributes to the ratelimit (12/minute) // TODO: find alternative that does not hit the ratelimit JSONObject obj = getJSONRelax("application/rate_limit_status"); return !obj.has("errors"); } }