summaryrefslogtreecommitdiff
path: root/src/io/BearerRequester.java
blob: 4cb554b4deca313a4aa7d4bcd9239f5b4d9b38fd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
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);
        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);
        }
    }

    @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");
    }
}