Skip to content

Latest commit

 

History

History
257 lines (201 loc) · 10.9 KB

TwitterAndSignpost.md

File metadata and controls

257 lines (201 loc) · 10.9 KB

Using Signpost with Twitter

You can get a consumer key and secret for your Twitter application here.

Simple Example - Setting up authentication and GET'ing mentions

        OAuthConsumer consumer = new DefaultOAuthConsumer(
                // the consumer key of this app (replace this with yours)
                "iIlNngv1KdV6XzNYkoLA",
                // the consumer secret of this app (replace this with yours)
                "exQ94pBpLXFcyttvLoxU2nrktThrlsj580zjYzmoM");

        OAuthProvider provider = new DefaultOAuthProvider(
                "http://twitter.com/oauth/request_token",
                "http://twitter.com/oauth/access_token",
                "http://twitter.com/oauth/authorize");

        /****************************************************
         * The following steps should only be performed ONCE
         ***************************************************/

        // we do not support callbacks, thus pass OOB
        String authUrl = provider.retrieveRequestToken(consumer, OAuth.OUT_OF_BAND);

        // bring the user to authUrl, e.g. open a web browser and note the PIN code
        // ...         

        String pinCode = // ... you have to ask this from the user, or obtain it
        // from the callback if you didn't do an out of band request

        // user must have granted authorization at this point
        provider.retrieveAccessToken(consumer, pinCode);

        // store consumer.getToken() and consumer.getTokenSecret(),
        // for the current user, e.g. in a relational database
        // or a flat file
        // ...

        /****************************************************
         * The following steps are performed everytime you
         * send a request accessing a resource on Twitter
         ***************************************************/

        // if not yet done, load the token and token secret for
        // the current user and set them
        consumer.setTokenWithSecret(ACCESS_TOKEN, TOKEN_SECRET);

        // create a request that requires authentication
        URL url = new URL("http://twitter.com/statuses/mentions.xml");
        HttpURLConnection request = (HttpURLConnection) url.openConnection();

        // sign the request
        consumer.sign(request);

        // send the request
        request.connect();

        // response status should be 200 OK
        int statusCode = request.getResponseCode();

Here is what you typically do step by step:

  1. When your users want to access Twitter through your application for the first time, you should display some kind of notification which indicates that an authentication process is about to start
  2. Call OAuthProvider.retrieveRequestToken(callback) on the OAuthProvider. If your application can receive callbacks via URLs, you can be informed about successful authorization by providing a callback URL here. If you cannot receive callbacks (e.g. because you're developing a desktop application), then you must pass "oob" here (for "out of band").
  3. On method return, you have to send the user to the URL returned by it. The user now must grant your application access to Twitter - this step is out of the reach of your application, because it happens in the Web browser.
  4. Call OAuthProvider.retrieveAccessToken() with the PIN code generated by Twitter in the previous step. If your interaction style is out-of-band, then you must ask the user to enter this value somewhere, since there is no way to get it programatically. If however you defined a callback URL before, then the PIN code will be passed to your application automatically with that callback as an "oauth_verifier" parameter.
  5. On method return, call OAuthConsumer.getToken() and OAuthConsumer.getTokenSecret(), associate them with the user who triggered the authorization procedure, and store them away safely.
  6. Any OAuthConsumer configured with these values can now sign HTTP requests in order to access protected resources on Twitter on behalf of that user.

You can also download this example code as a Java/Eclipse project and go from there. This example application executes all steps above sequentially in the console. If run successfully, its output should be something like this:

Fetching request token from Twitter...
Request token: g1re4iYfknOYMB62JZkddjwhCfvfn4WPdZrzgscMOA
Token secret: wKStK0dmhPcuFxBki3imJv7yVNXgRndmW4LSmuCg
Now visit:
http://twitter.com/oauth/authorize?oauth_token=g1re4iYfknOYMB62JZkddjwhCfvfn4WPdZrzgscMOA
... and grant this app authorization
Enter the PIN code and hit ENTER when you're done:
xxxxxxx
Fetching access token from Twitter...
Access token: 14418463-Xt70aZ8b2jz19MzY7JnQJ6HglPh0Hc5p939gLH5YI
Token secret: TkG689FOx7amgdX2ta6epE5MYsmZxVuO9ith7FtJrs
Sending request to Twitter...
Response: 200 OK

Elaborate Example - POST'ing status updates

Thanks Kerry for sharing this via our Google group!

NOTE: This example assumes that you're using Apache HttpClient for HTTP messaging.

We'll need to tweak some of HttpClient's defaults, like so:

    public HttpParams getParams() {
        // Tweak further as needed for your app
        HttpParams params = new BasicHttpParams();
        // set this to false, or else you'll get an
        // Expectation Failed: error
        HttpProtocolParams.setUseExpectContinue(params, false);
        return params;
    }

    // Your actual method might look like this:
    public void updateProfileBackground(User user, File file) {
        try {
            // Create a new consumer using the commons implementation
            OAuthConsumer consumer = new CommonsHttpOAuthConsumer(consumerKey,
                    consumerSecret);
            consumer.setTokenWithSecret(getUserAccessToken(user),
                    getUserTokenSecret(user));
            HttpPost uploadBackgroundPost = new HttpPost(
                    UPDATE_PROFILE_BACKGROUND_IMAGE_URL);

            // The body of a multi-part post isn't needed
            // for the generation of the signature
            consumer.sign(uploadBackgroundPost);

            // only works in strict mode
            MultipartEntity entity = new MultipartEntity(
                    HttpMultipartMode.STRICT);

            // Twitter checks against supported file types
            FileBody imageBody = new FileBody(file, "image/png");

            entity.addPart("image", imageBody);
            uploadBackgroundPost.setEntity(entity);
            DefaultHttpClient httpClient = new DefaultHttpClient(getParams());

            // If you're interested in the headers,
            // implement and add a request interceptor that prints them
            httpClient.addRequestInterceptor(new PrintRequestInterceptor());

            System.out.println(httpClient.execute(uploadBackgroundPost,
                    new BasicResponseHandler()));
        } catch (Exception e) {
            // do some proper exception handling here
            e.printStackTrace();
        }
    }

Now oddly enough, if the multi-part form data is not being formatted exactly to Twitter's preferences you won't get an error message, but the background image will be set to a broken link! You might see the image url being constructed as:

http://a3.twimg.com/profile_background_images/test.png

instead of:

http://a3.twimg.com/profile_background_images/5463125/test.png (Notice the missing folder)

Instructions for using Apache HttpClient 2.x

Additionally here's an example if you're using Apache HttpClient2.x, which uses a different API than HttpClient 4.x.

First off you'll need to make your own adapter for the MultipartPostMethod (or if you're adventurous HttpMethod more generally).

Something along the lines of:

    public class HttpClient2OAuthConsumer extends AbstractOAuthConsumer {
        public HttpClient2OAuthConsumer(String consumerKey,
                String consumerSecret, SignatureMethod signatureMethod) {
            super(consumerKey, consumerSecret, signatureMethod);
        }

        protected oauth.signpost.http.HttpRequest wrap(Object request) {
            if (!(request instanceof MultipartPostMethod))
                throw new IllegalArgumentException(
                        "This consumer expects requests of type MultipartPostMethod");
            else
                return new HttpRequestAdapter((MultipartPostMethod) request);
        }
    }

And:

    public class HttpRequestAdapter implements oauth.signpost.http.HttpRequest {

        private MultipartPostMethod method;

        public HttpRequestAdapter(MultipartPostMethod method) {
            this.method = method;
        }

        public String getContentType() {
            Header contentHeader = method.getRequestHeader("Content-Type");
            return (contentHeader == null) ? "" : contentHeader.getValue();
        }

        public String getHeader(String s) {
            return method.getRequestHeader(s).getValue();
        }

        public InputStream getMessagePayload() throws IOException {
            return new FileInputStream(method.getFileData());
        }

        public String getMethod() {
            return method.getName();
        }

        public String getRequestUrl() {
            try {
                return method.getURI().getURI();
            } catch (URIException e) {
                e.printStackTrace();
            }
            return null;
        }

        public void setHeader(String s, String s1) {
            method.setRequestHeader(s, s1);
        }

    }

This method will look very similar.

    public void updateProfileBackgroundHttpCommons(User user, File file) {
        try {
            HttpClient client = new HttpClient();
            // make a new consumer with our own implementation
            OAuthConsumer consumer = new HttpClient2OAuthConsumer(consumerKey,
                    consumerSecret);
            consumer.setTokenWithSecret(getUserAccessToken(user),
                    getUserTokenSecret(user));
            MultipartPostMethod mp = new MultipartPostMethod(
                    UPDATE_PROFILE_BACKGROUND_IMAGE_URL);

            FilePart image = new FilePart("image", file, "image/png", null); // again specify the type
            // charset cannot be set to a type.
            // httpclient2.x mistakenly adds one to a filepart by default
            image.setCharSet(null);
            mp.addPart(image);
            mp.setStrictMode(false);

            consumer.sign(mp);
            client.executeMethod(mp);
            for (Header header : mp.getRequestHeaders()) {
                System.out.println(header.toExternalForm());
            }
            System.out.println(mp.getResponseBodyAsString());

        } catch (Exception e) {
            // do some proper exception handling here
            e.printStackTrace();
        }
    }

And that's it.