OAuth, twitter4j, and JavaFX

I’ve started developing a text-only client for Twitter. The impulse came from getting fed up with all the auto-playing promoted tweets that were clogging my timeline. I looked around and found a library to handle all the Twitter API calls and started coding. The example code was all a bit bare-bones, but mostly it was straightforward. The one thing so far that I think could use a bit more explanation is the login process, and that’s the point of this post.

There are tons of tutorials for how to do OAuth in a web app, but there are precious few that deal with doing authentication from within a desktop application without resorting to switching out to a web browser. This method gets around that problem by using the built-in web browser component in JavaFX, the WebView.

First, I created a simple FXML file with a GridPane that contains a WebView inside a ScrollPane, a TextField, and a Button. The WebView is for displaying the Twitter OAuth screens.

In the controller class, I initialize the WebView with the request for access:

@Override
public void initialize(URL location, ResourceBundle resources) {
    try {
        // get the initial OAuth request token
        _twitter = TwitterFactory.getSingleton();
        _requestToken = _twitter.getOAuthRequestToken();
        // fetch the auth URL (in a WebView, to let the user login and then grant authorization to the app)
        _view.getEngine().load(_requestToken.getAuthorizationURL());
    } catch (Exception e) {
        __l.error("Exception trying to perform OAuth login", e);
    }

}

It turns out that when the user grants access, Twitter displays a multi-digit number. What needs to happen is, the user copies the number into the TextField and clicks the Ok button. Here’s that handler:

@FXML
@SuppressWarnings("unused")
public void handleOK(ActionEvent evt) {
    String oauthPIN = _pin.getText();
    AccessToken accessToken;
    try {
        if (!oauthPIN.isEmpty()) {
            accessToken = _twitter.getOAuthAccessToken(_requestToken, oauthPIN);
        } else {
            accessToken = _twitter.getOAuthAccessToken();
        }
        // now we have an AccessToken, we send it to the callback for application use and storage
        _tokenConsumer.accept(accessToken, _stage);
    } catch (TwitterException te) {
        if (401 == te.getStatusCode()) {
            __l.error("Unable to get the access token.", te);
        } else {
            __l.error("Exception getting access token", te);
        }
    }

}

The callback gets registered during setup; it’s a method on the main window that accepts the AccessToken and persists it into the app’s preferences, and closes the login window.

Published by pirateguillermo

I play the bagpipes. I program computers. I support my family in their various endeavors, and I enjoy my wonderful life.

Leave a Reply